pax_global_header00006660000000000000000000000064142154344410014514gustar00rootroot0000000000000052 comment=a126eef49f27f0c704f93ab93f40d4cc78ce47fd gnome-shell-extension-gsconnect-50/000077500000000000000000000000001421543444100175075ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/.editorconfig000066400000000000000000000012371421543444100221670ustar00rootroot00000000000000# Topmost editorconfig for project, don't ascend # to parent directories when scanning configs root = true # All files: UTF-8 with Unix-style newlines, # and a newline at the end of the file [*] insert_final_newline = true end_of_line = lf charset = utf-8 # JavaScript, Python, CSS: 4-space indents [*.{js,py,css}] indent_style = space indent_size = 4 # XML, Meson, Glade: 2-space indents [*.{xml,build,ui}] indent_style = space indent_size = 2 # Most JSON: 2-space indents [*.json] indent_style = space indent_size = 2 # WebExtension templates: 4-space indents [data/org.gnome.shell.extensions.gsconnect.json-{chrome,mozilla}] indent_style = space indent_size = 4 gnome-shell-extension-gsconnect-50/.eslintrc.json000066400000000000000000000175161421543444100223150ustar00rootroot00000000000000{ "env": { "es6": true }, "extends": "eslint:recommended", "rules": { "array-bracket-newline": [ "error", "consistent" ], "array-bracket-spacing": [ "error", "never" ], "array-callback-return": "error", "arrow-spacing": "error", "block-scoped-var": "error", "block-spacing": "error", "brace-style": "error", "comma-dangle": [ "error", { "arrays": "always-multiline", "objects": "always-multiline", "functions": "never" } ], "comma-spacing": [ "error", { "before": false, "after": true } ], "comma-style": [ "error", "last" ], "computed-property-spacing": "error", "curly": [ "error", "multi-or-nest", "consistent" ], "dot-location": [ "error", "property" ], "eol-last": "error", "eqeqeq": "error", "func-call-spacing": "error", "func-name-matching": "error", "func-style": [ "error", "declaration", { "allowArrowFunctions": true } ], "indent": [ "error", 4, { "ignoredNodes": [ "CallExpression[callee.object.name=GObject][callee.property.name=registerClass] > ClassExpression:first-child" ], "MemberExpression": "off", "SwitchCase": 1 } ], "key-spacing": [ "error", { "beforeColon": false, "afterColon": true } ], "keyword-spacing": [ "error", { "before": true, "after": true } ], "linebreak-style": [ "error", "unix" ], "lines-between-class-members": "error", "max-nested-callbacks": "error", "max-statements-per-line": "error", "new-parens": "error", "no-array-constructor": "error", "no-caller": "error", "no-constant-condition": [ "error", { "checkLoops": false } ], "no-empty": [ "error", { "allowEmptyCatch": true } ], "no-extra-bind": "error", "no-implicit-coercion": [ "error", { "allow": [ "!!" ] } ], "no-iterator": "error", "no-label-var": "error", "no-lonely-if": "error", "no-loop-func": "error", "no-nested-ternary": "error", "no-new-object": "error", "no-new-wrappers": "error", "no-octal-escape": "error", "no-proto": "error", "no-prototype-builtins": "off", "no-restricted-properties": [ "error", { "object": "Lang", "property": "bind", "message": "Use arrow notation or Function.prototype.bind()" }, { "object": "Lang", "property": "Class", "message": "Use ES6 classes" }, { "object": "imports", "property": "mainloop", "message": "Use GLib main loops and timeouts" } ], "no-restricted-syntax": [ "error", { "selector": "MethodDefinition[key.name=\"_init\"] > FunctionExpression[params.length=1] > BlockStatement[body.length=1] CallExpression[arguments.length=1][callee.object.type=\"Super\"][callee.property.name=\"_init\"] > Identifier:first-child", "message": "_init() that only calls super._init() is unnecessary" }, { "selector": "MethodDefinition[key.name=\"_init\"] > FunctionExpression[params.length=0] > BlockStatement[body.length=1] CallExpression[arguments.length=0][callee.object.type=\"Super\"][callee.property.name=\"_init\"]", "message": "_init() that only calls super._init() is unnecessary" } ], "no-return-assign": "error", "no-return-await": "error", "no-self-compare": "error", "no-shadow-restricted-names": "error", "no-spaced-func": "error", "no-tabs": "error", "no-template-curly-in-string": "error", "no-throw-literal": "error", "no-trailing-spaces": "error", "no-undef-init": "error", "no-unneeded-ternary": "error", "no-unused-expressions": "error", "no-unused-vars": [ "error", { "args": "none", "vars": "local" } ], "no-useless-call": "error", "no-useless-computed-key": "error", "no-useless-concat": "error", "no-useless-constructor": "error", "no-useless-rename": "error", "no-useless-return": "error", "no-whitespace-before-property": "error", "no-with": "error", "nonblock-statement-body-position": [ "error", "below" ], "object-curly-newline": [ "error", { "consistent": true } ], "object-curly-spacing": "error", "operator-assignment": "error", "operator-linebreak": "error", "prefer-const": "error", "prefer-numeric-literals": "error", "prefer-promise-reject-errors": "error", "prefer-rest-params": "error", "prefer-spread": "error", "quotes": [ "error", "single", { "avoidEscape": true } ], "require-await": "error", "rest-spread-spacing": "error", "semi": [ "error", "always" ], "semi-spacing": [ "error", { "before": false, "after": true } ], "semi-style": "error", "space-before-blocks": "error", "space-before-function-paren": [ "error", { "named": "never", "anonymous": "always", "asyncArrow": "always" } ], "space-in-parens": "error", "space-infix-ops": [ "error", { "int32Hint": false } ], "space-unary-ops": "error", "spaced-comment": "error", "switch-colon-spacing": "error", "symbol-description": "error", "template-curly-spacing": "error", "template-tag-spacing": "error", "unicode-bom": "error", "valid-jsdoc": [ "error", { "requireReturn": false } ], "wrap-iife": [ "error", "inside" ], "yield-star-spacing": "error", "yoda": "error" }, "globals": { "ARGV": "readonly", "Debugger": "readonly", "GIRepositoryGType": "readonly", "globalThis": "readonly", "imports": "readonly", "Intl": "readonly", "log": "readonly", "logError": "readonly", "print": "readonly", "printerr": "readonly", "global": false, "debug": false, "_": false, "_C": false, "_N": false, "ngettext": false, "HAVE_WAYLAND": false, "HAVE_REMOTEINPUT": false }, "parserOptions": { "ecmaVersion": 2017 } } gnome-shell-extension-gsconnect-50/.github/000077500000000000000000000000001421543444100210475ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/.github/ISSUE_TEMPLATE/000077500000000000000000000000001421543444100232325ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000030411421543444100257220ustar00rootroot00000000000000--- name: Bug report about: Report an issue to help us improve GSConnect title: '' labels: '' assignees: '' --- > ***ATTENTION**: GSConnect only supports the latest, stable version of GNOME. We are no longer accepting bug reports for previous versions.* **Describe the bug** A clear and concise description of what the bug is. **Steps To Reproduce:** 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. You can drag-and-drop or cut-and-paste images directly into this edit window, to include them in your report. **Support Log** Please generate a support log ([Instructions](../wiki/Help#generate-support-log)) and paste any messages related to this issue between the two ``` lines below. ``` ``` **System Details (please complete the following information):** - **GSConnect version:** [e.g. 35, 36, ... (displayed in the GSConnect "About" window)] - **Installed from:** [e.g. GNOME Extensions Website, GitHub, Package Manager, ...] - **GNOME/Shell version:** [e.g. 3.36, ...] - **Distro/Release:** [e.g. Ubuntu 18.04, Fedora 29, Arch, ...] **GSConnect environment (if applicable):** - **Paired Device(s):** [e.g. Galaxy Note 8, Pixel 2, Honor 9, ...] - **KDE Connect app version:** [e.g. 1.12.7] - **Plugin(s):** [if the issue only occurs when using certain plugin(s)] **Additional Notes:** Add any additional information about the problem or your system. gnome-shell-extension-gsconnect-50/.github/ISSUE_TEMPLATE/feature-request-or-idea.md000066400000000000000000000027421421543444100302200ustar00rootroot00000000000000--- name: Feature request or idea about: Suggest an improvement to GSConnect title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **System Details (please complete the following information):** - GSConnect version: [e.g. 16, 15, ... (displayed in the GSConnect "About" window)] - Installed from: [e.g. extensions.gnome.org, GitHub Release download, distro package, ...] **Additional context** Add any other context for your suggestion here, including screenshots if applicable. #### Please delete this line and everything below before submitting your request GSConnect works in concert with the [KDE Connect][1] app on paired Android devices. Communication between GSConnect and the device is controlled by **KDE Connect**, _not_ GSConnect. Any request that would require features not supported in KDE Connect, or would require GSConnect to deviate from the KDE Connect protocols, cannot be implemented. Any such requests should be made to the KDE Connect project. The requested functionality can only be implemented in GSConnect after support is added to KDE Connect. [1]: (https://community.kde.org/KDEConnect) gnome-shell-extension-gsconnect-50/.github/workflows/000077500000000000000000000000001421543444100231045ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/.github/workflows/gnome-3-36.yml000066400000000000000000000024111421543444100253200ustar00rootroot00000000000000name: gnome-3-36 on: push: branches: [ gnome-3-36 ] pull_request: branches: [ gnome-3-36 ] jobs: test: runs-on: ubuntu-latest container: image: andyholmes/gsconnect-ci:3.36 steps: - uses: actions/checkout@v2 - name: Prepare run: | meson --prefix=/usr \ --libdir=lib/ \ -Dgnome_shell_libdir=/usr/lib64/ \ -Dpost_install=true \ _build - name: Uninstalled Tests run: | meson test -C _build --suite gsconnect:data \ --suite gsconnect:lint \ --print-errorlogs - name: Installed Tests run: | ninja -C _build install xvfb-run -a dbus-run-session -- \ gnome-desktop-testing-runner gsconnect -L _build/meson-logs - name: Upload Test Logs if: ${{ always() }} uses: actions/upload-artifact@v2 with: name: test-logs path: _build/meson-logs - name: Test Build run: | ninja -C _build make-zip - name: Upload Test Build uses: actions/upload-artifact@v2 with: name: test-build path: _build/gsconnect@andyholmes.github.io.zip gnome-shell-extension-gsconnect-50/.github/workflows/gnome-3-38.yml000066400000000000000000000024101421543444100253210ustar00rootroot00000000000000name: gnome-3-38 on: push: branches: [ gnome-3-38 ] pull_request: branches: [ gnome-3-38 ] jobs: test: runs-on: ubuntu-latest container: image: gsconnect/gsconnect-ci:3.38 steps: - uses: actions/checkout@v2 - name: Prepare run: | meson --prefix=/usr \ --libdir=lib/ \ -Dgnome_shell_libdir=/usr/lib64/ \ -Dpost_install=true \ _build - name: Uninstalled Tests run: | meson test -C _build --suite gsconnect:data \ --suite gsconnect:lint \ --print-errorlogs - name: Installed Tests run: | ninja -C _build install xvfb-run -a dbus-run-session -- \ gnome-desktop-testing-runner gsconnect -L _build/meson-logs - name: Upload Test Logs if: ${{ always() }} uses: actions/upload-artifact@v2 with: name: test-logs path: _build/meson-logs - name: Test Build run: | ninja -C _build make-zip - name: Upload Test Build uses: actions/upload-artifact@v2 with: name: test-build path: _build/gsconnect@andyholmes.github.io.zip gnome-shell-extension-gsconnect-50/.github/workflows/main.yml000066400000000000000000000024311421543444100245530ustar00rootroot00000000000000name: CI on: push: branches: - master pull_request: branches: - master workflow_dispatch: jobs: test: runs-on: ubuntu-latest container: image: gsconnect/gsconnect-ci:latest steps: - uses: actions/checkout@v2 - name: Prepare run: | meson --prefix=/usr \ --libdir=lib/ \ -Dgnome_shell_libdir=/usr/lib64/ \ -Dpost_install=true \ _build - name: Uninstalled Tests run: | meson test -C _build --suite gsconnect:data \ --suite gsconnect:lint \ --print-errorlogs - name: Installed Tests run: | ninja -C _build install xvfb-run -a dbus-run-session -- \ gnome-desktop-testing-runner gsconnect -L _build/meson-logs - name: Upload Test Logs if: ${{ always() }} uses: actions/upload-artifact@v2 with: name: test-logs path: _build/meson-logs - name: Test Build run: | ninja -C _build make-zip - name: Upload Test Build uses: actions/upload-artifact@v2 with: name: test-build path: _build/gsconnect@andyholmes.github.io.zip gnome-shell-extension-gsconnect-50/.gitignore000066400000000000000000000000121421543444100214700ustar00rootroot00000000000000_build *~ gnome-shell-extension-gsconnect-50/CODE_OF_CONDUCT.md000066400000000000000000000003301421543444100223020ustar00rootroot00000000000000# Code of Conduct While taking part in this project, all that is required is to stay on topic. Note that you are still bound by the Code of Conduct for whichever platform you use to access the project repository. gnome-shell-extension-gsconnect-50/CONTRIBUTING.md000066400000000000000000000140521421543444100217420ustar00rootroot00000000000000# Contributing Thank you for considering contributing to this project. It means that you not only find it useful, but that you think there's something that could be done to make it more useful, or useful to more people. The goal is to create an implementation of KDE Connect that integrates with the GNOME desktop more than is appropriate for the original implementation. ## Code of Conduct While taking part in this project, all that is required is to stay on topic. Note that you are still bound by the Code of Conduct for whichever platform you use to access the project repository. ## Overview This document is mostly about code contributions. There are pages in the Wiki for [Translating][translating], [Theming][theming] and [Packaging][packaging]. You can open a [New Issue][issue] or [Pull Request][pr] for anything you like and it will be reviewed. ### Code Guidelines * Code MUST be written in [GJS][gjs] if at all possible Obvious exceptions are code that help integrate with other programs, like the Nautilus extension. * Code MUST run anywhere GNOME Shell runs It is acceptable and sometimes necessary to use resources that may be specific to Linux, but fallbacks must be available for other systems like BSD. Virtual machines may be supported, but not at any expense to real systems. * Code MUST NOT break compatibility with the KDE Connect project Under no circumstances may code break protocol compatibility or introduce new protocol features. Any protocol related discussion must happen directly with the KDE Connect team and changes or additions are subject to their approval. ### Code Style GSConnect ships with an ESLint file, which is run on all code by the CI and can be run on code simply with `eslint src/`. When in doubt, copy the existing code style. ## Developing ### Architecture GSConnect is composed of three relatively distinct components: * Service (`/service`) The service runs as a separate process in the background and does all the heavy lifting, including connecting and communicating with remote devices. It also exposes several DBus interfaces. * Shell Extension (`extension.js`, `/shell`) The GNOME Shell extension controls starting and stopping the service, and consumes the DBus interfaces exposed by the service. It also helps GSConnect to integrate into GNOME Shell. * Preferences (`gsconnect-preferences`, `/preferences`) Unlike most extensions, GSConnect has it's own process for configuring the service and devices, which also communicates with the service over DBus. ### Building and Installing GSConnect uses a [`meson`][meson] build system which can accomodate system or user installs. Typically, GSConnect should be developed and installed as a user extension, while support for system installs exists primarily for distributions that want to package GSConnect. #### User Install When installing as a user extension, GSConnect will try its best to detect necessary paths and automatically install required files required for the service to run. The example below will build a user extension ZIP, then install it: ```sh $ meson _build $ ninja -C _build install-zip ``` Use the `make-zip` target instead to simple build a distributable ZIP, which will be output as `_build/gsconnect@andyholmes.github.io.zip`: ```sh $ meson _build $ ninja -C _build make-zip ``` #### System Install When installing as a system extension, the build must be configured to ensure GSConnect can function properly when run (details in `meson_options.txt`). Below is a typical example for Fedora: ```sh $ meson --prefix=/usr \ --libdir=lib/ \ -Dgnome_shell_libdir=/usr/lib64/ \ -Dfirewalld=true \ -Dpost_install=true \ _build $ ninja -C _build install ``` ### Typical Workflow The typical workflow for developing GSConnect will mainly involve working on the service. First build and install the extension: ```sh $ meson _build $ ninja -C _build install-zip ``` Next restart GNOME Shell and enable the extension. While developing you should enable debugging output and watch the output with `journalctl`: ```sh $ dconf write /org/gnome/shell/extensions/gsconnect/debug true $ journalctl -f -o cat /usr/bin/gjs ``` After making changes to the service, you should run the `install-zip` target again. The service will automatically restart and there is no need to restart GNOME Shell unless you have made changes to the shell extension: ```sh $ ninja -C _build install-zip ``` #### Preferences When working on the Preferences application, you must close and reopen the window after running the `install-zip` target. You can use `journalctl` to watch the output or if it's more convenient simply run the application from the shell: ```sh $ cd ~/.local/share/gnome-shell/extensions/gsconnect@andyholmes.github.io $ ./gsconnect-preferences ``` #### Shell Extension When developing the Shell extension, you must restart GNOME Shell after making any changes. Note that the `debug()` function is not available in the Shell extension and you should watch `gnome-shell` with `journalctl` instead of GJS: ```sh $ journalctl -f -o cat /usr/bin/gnome-shell ``` ## Questions For general discussion, there is an IRC/Matrix channel for GSConnect: * Matrix: https://matrix.to/#/#_gimpnet_#gsconnect:matrix.org * IRC: irc://irc.gimp.org/#gsconnect If that's not convenient, discussion can happen in the comments to your [Pull Request][pr] or you can open a [New Issue][issue] for more public discussion and mark the Pull Request as a fix for it. [design]: https://wiki.gnome.org/Projects/GnomeShell/Design/Principles [hig]: https://developer.gnome.org/hig/stable/ [translating]: https://github.com/GSConnect/gnome-shell-extension-gsconnect/wiki/Translating [packaging]: https://github.com/GSConnect/gnome-shell-extension-gsconnect/wiki/Packaging [theming]: https://github.com/GSConnect/gnome-shell-extension-gsconnect/wiki/Theming [issue]: https://github.com/GSConnect/gnome-shell-extension-gsconnect/issues [pr]: https://github.com/GNOME/gnome-shell/pulls [gjs]: https://gitlab.gnome.org/GNOME/gjs/wikis/home [meson]: https://mesonbuild.com/ gnome-shell-extension-gsconnect-50/LICENSE000066400000000000000000000431761421543444100205270ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {description} Copyright (C) {year} {fullname} This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. {signature of Ty Coon}, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. gnome-shell-extension-gsconnect-50/README.md000066400000000000000000000032641421543444100207730ustar00rootroot00000000000000[GSConnect][ego] is a complete implementation of [KDE Connect][kdeconnect] especially for GNOME Shell with Nautilus, [Chrome][chrome] and [Firefox][firefox] integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows. With GSConnect you can securely connect to mobile devices and other desktops to: * Share files, links and text * Send and receive messages * Sync clipboard content * Sync contacts * Sync notifications * Control media players * Control system volume * Execute predefined commands * And more… Please see the **[Wiki][wiki]** for more information about **[Features][features]** and **[Help][help]**. [Get it on GNOME Extensions][ego] [Available in the Chrome Web Store][chrome] [Get the Add-On][firefox] [ego]: https://extensions.gnome.org/extension/1319/gsconnect/ [chrome]: https://chrome.google.com/webstore/detail/gsconnect/jfnifeihccihocjbfcfhicmmgpjicaec [firefox]: https://addons.mozilla.org/firefox/addon/gsconnect/ [kdeconnect]: https://userbase.kde.org/KDEConnect [wiki]: https://github.com/GSConnect/gnome-shell-extension-gsconnect/wiki/ [features]: https://github.com/GSConnect/gnome-shell-extension-gsconnect/wiki/Features [help]: https://github.com/GSConnect/gnome-shell-extension-gsconnect/wiki/Help gnome-shell-extension-gsconnect-50/RELEASE_CHECKLIST.md000066400000000000000000000013131421543444100225000ustar00rootroot00000000000000# Release Checklist ## Preparing for a new release - [ ] Bump version in `meson.build` - [ ] Bump version in `data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in` - [ ] Run `meson _build` - [ ] Run `meson test -C _build` - [ ] Make a commit and push it ## Release: Github - [ ] Run `meson _build` - [ ] Run `ninja -C _build make-zip` - [ ] Tag a new release with notes at `https://github.com/GSConnect/gnome-shell-extension-gsconnect/releases/new` - [ ] Add `_build/gsconnect@andyholmes.github.io.zip` to the release ## Release: EGO - [ ] Run `meson _build` - [ ] Run `ninja -C _build make-zip` - [ ] Upload `_build/gsconnect@andyholmes.github.io.zip` to `https://extensions.gnome.org/upload` gnome-shell-extension-gsconnect-50/build-aux/000077500000000000000000000000001421543444100214015ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/build-aux/ego/000077500000000000000000000000001421543444100221535ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/build-aux/ego/mkzip.sh000077500000000000000000000016771421543444100236570ustar00rootroot00000000000000#!/bin/sh export DESTDIR="${MESON_BUILD_ROOT}/_zip" ZIP_DIR="${MESON_BUILD_ROOT}/${UUID}" ZIP_FILE="${MESON_BUILD_ROOT}/${UUID}.zip" # PRE-CLEAN rm -rf ${DESTDIR} ${ZIP_DIR} ${ZIP_FILE} # BUILD if ! ninja -C ${MESON_BUILD_ROOT} install > /dev/null; then exit 1; fi # COPY mkdir -p ${ZIP_DIR} cp -pr ${DESTDIR}/${DATADIR}/gnome-shell/extensions/${UUID}/* ${ZIP_DIR} cp -pr ${DESTDIR}/${DATADIR}/nautilus-python/extensions/* ${ZIP_DIR} cp -pr ${DESTDIR}/${LOCALEDIR} ${ZIP_DIR} cp -pr ${DESTDIR}/${GSCHEMADIR} ${ZIP_DIR} glib-compile-schemas ${ZIP_DIR}/schemas # COMPRESS cd ${ZIP_DIR} zip -qr ${ZIP_FILE} . echo "Extension saved to ${ZIP_FILE}" # INSTALL if [ "$INSTALL" = true ]; then EXTENSIONS_DIR="${HOME}/.local/share/gnome-shell/extensions" INSTALL_DIR="${EXTENSIONS_DIR}/${UUID}" mkdir -p ${EXTENSIONS_DIR} rm -rf ${INSTALL_DIR} unzip -q ${ZIP_FILE} -d ${INSTALL_DIR} echo "Extension installed to ${INSTALL_DIR}" fi gnome-shell-extension-gsconnect-50/build-aux/meson/000077500000000000000000000000001421543444100225225ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/build-aux/meson/post-install.sh000077500000000000000000000001311421543444100255050ustar00rootroot00000000000000#!/bin/sh GSCHEMA_DIR="${DESTDIR}/${GSCHEMADIR}" glib-compile-schemas "${GSCHEMA_DIR}" gnome-shell-extension-gsconnect-50/crowdin.yml000066400000000000000000000011511421543444100216750ustar00rootroot00000000000000project_identifier: org.gnome.Shell.Extensions.GSConnect files: - source: /po/org.gnome.Shell.Extensions.GSConnect.pot translation: /po/%locale%.po languages_mapping: locale: ar-SA: ar ca-ES: ca ca: ca fr: fr da: da de: de pt-BR: pt_BR nl: nl_NL nl-BE: nl_BE es-ES: es be: be cs: cs et: et gl: gl hu: hu it: it ja: ja lt: lt pl: pl ru: ru sk: sk sr: sr tr: tr uk: uk zh-CN: zh_CN zh-TW: zh_TW gnome-shell-extension-gsconnect-50/data/000077500000000000000000000000001421543444100204205ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/data/application.css000066400000000000000000000037721421543444100234460ustar00rootroot00000000000000 /** * Chrome/Firefox buttons in Settings Window */ .badge-button { border: 0px; padding: 0px; background: transparent; } /** * Placeholders */ .placeholder { background: @content_view_bg_color; margin-bottom: 30px; } .placeholder-image { margin-bottom: 16px; opacity: 0.5; } .placeholder-title { font-size: larger; font-weight: bold; margin-bottom: 3px; opacity: 0.5; } .placeholder-description { font-size: smaller; opacity: 0.5; } /** * GSConnectContactChooser */ .contact-window { border-top: 1px solid @borders; } .contact-list row { padding: 0px; } /** * GSConnectConversationWidget */ .message-scrolled { border-bottom: 1px solid @borders; } /** * GSConnectMessagingWindow */ .message-list > label { font-size: small; margin-bottom: 1em; } .thread-list { background: @content_view_bg_color; } /** * Incoming Message Bubbles (GtkLabel subclass) */ .message-in { color: @theme_fg_color; background: alpha(@theme_selected_bg_color, 0.2); border: 1px solid alpha(@theme_selected_bg_color, 0.2); border-radius: 5px; box-shadow: 2px 2px 3px -2px rgba(0, 0, 0, 0.75); padding: 6px 9px 6px 9px; } .message-in selection { color: @theme_selected_bg_color; background-color: @theme_selected_fg_color; } /** * Outgoing Message Bubbles (GtkLabel subclass) */ .message-out { color: @theme_fg_color; background-color: @theme_bg_color; border: 1px solid alpha(@theme_fg_color, 0.1); border-radius: 5px; box-shadow: 2px 2px 3px -2px rgba(0, 0, 0, 0.75); caret-color: currentColor; -gtk-secondary-caret-color: currentColor; padding: 6px 9px 6px 9px; } .message-out selection { background-color: @theme_selected_bg_color; color: @theme_selected_fg_color; } /** * Must contrast with message background */ .message-in *:link, .message-out *:link { color: @theme_fg_color; } /** * Special case for pending messages */ .message-pending .message-out { opacity: 0.5; } /** * Error Dialog */ .error-stack-frame > * { border-top-width: 0; } gnome-shell-extension-gsconnect-50/data/config.js.in000066400000000000000000000011071421543444100226270ustar00rootroot00000000000000var PACKAGE_VERSION = @PACKAGE_VERSION@; var PACKAGE_URL = '@PACKAGE_URL@'; var PACKAGE_BUGREPORT = '@PACKAGE_BUGREPORT@'; var PACKAGE_DATADIR = '@PACKAGE_DATADIR@'; var PACKAGE_LOCALEDIR = '@PACKAGE_LOCALEDIR@'; var GSETTINGS_SCHEMA_DIR = '@GSETTINGS_SCHEMA_DIR@'; var GNOME_SHELL_LIBDIR = '@GNOME_SHELL_LIBDIR@'; var APP_ID = '@APPLICATION_ID@'; var APP_PATH = '@APPLICATION_PATH@'; var IS_USER = false; // External binary paths var OPENSSL_PATH = '@OPENSSL_PATH@'; var SSHADD_PATH = '@SSHADD_PATH@'; var SSHKEYGEN_PATH = '@SSHKEYGEN_PATH@'; var FFMPEG_PATH = '@FFMPEG_PATH@'; gnome-shell-extension-gsconnect-50/data/firewalld/000077500000000000000000000000001421543444100223715ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/data/firewalld/gsconnect.xml000066400000000000000000000004701421543444100250770ustar00rootroot00000000000000 GSConnect KDE Connect implementation for GNOME gnome-shell-extension-gsconnect-50/data/firewalld/meson.build000066400000000000000000000002421421543444100245310ustar00rootroot00000000000000# firewalld Configuration if get_option('firewalld') install_data( 'gsconnect.xml', install_dir: join_paths(libdir, 'firewalld', 'services') ) endif gnome-shell-extension-gsconnect-50/data/icons/000077500000000000000000000000001421543444100215335ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/data/icons/computer-symbolic.svg000066400000000000000000000051511421543444100257330ustar00rootroot00000000000000 image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme gnome-shell-extension-gsconnect-50/data/icons/group-avatar-symbolic.svg000066400000000000000000000011121421543444100264760ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/icons/laptop-symbolic.svg000066400000000000000000000015051421543444100253730ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/icons/org.gnome.Shell.Extensions.GSConnect-symbolic.svg000066400000000000000000000065011421543444100330160ustar00rootroot00000000000000 image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme gnome-shell-extension-gsconnect-50/data/icons/org.gnome.Shell.Extensions.GSConnect.svg000066400000000000000000000010621421543444100311740ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/icons/phonelink-delete-symbolic.svg000066400000000000000000000106231421543444100273240ustar00rootroot00000000000000 image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme gnome-shell-extension-gsconnect-50/data/icons/phonelink-lock-symbolic.svg000066400000000000000000000111721421543444100270120ustar00rootroot00000000000000 image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme gnome-shell-extension-gsconnect-50/data/icons/phonelink-off-symbolic.svg000066400000000000000000000071341421543444100266370ustar00rootroot00000000000000 image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme gnome-shell-extension-gsconnect-50/data/icons/phonelink-ring-symbolic.svg000066400000000000000000000123651421543444100270260ustar00rootroot00000000000000 image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme gnome-shell-extension-gsconnect-50/data/icons/phonelink-setup-symbolic.svg000066400000000000000000000127751421543444100272340ustar00rootroot00000000000000 image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme gnome-shell-extension-gsconnect-50/data/icons/phonelink-symbolic.svg000066400000000000000000000065011421543444100260640ustar00rootroot00000000000000 image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme gnome-shell-extension-gsconnect-50/data/icons/smartphone-symbolic.svg000066400000000000000000000077261421543444100262670ustar00rootroot00000000000000 image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme gnome-shell-extension-gsconnect-50/data/icons/sms-send.svg000066400000000000000000000003001421543444100237760ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/icons/sms-symbolic.svg000066400000000000000000000011721421543444100246760ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/icons/tablet-symbolic.svg000066400000000000000000000007531421543444100253530ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/icons/tv-symbolic.svg000066400000000000000000000110421421543444100245220ustar00rootroot00000000000000 image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme gnome-shell-extension-gsconnect-50/data/images/000077500000000000000000000000001421543444100216655ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/data/images/chrome-badge.png000066400000000000000000000072621421543444100247170ustar00rootroot00000000000000‰PNG  IHDRĪ:ųfĖtEXtSoftwareAdobe ImageReadyqÉe<TIDATxŚģ]{pTW’v³›Ē†w§b ņ Sy:H)™é0ՎSŚØŌΈķČPkÅ©Z§ZLjXĘ@¤U¦6:Ö" "Æi”ĒR XK a7Łd³ėż»ßęģͽwY`S¾ßĢe7wĻėŽūż¾×9ēā9w¾õ[$2‚Ē N¬lĀ8¹Aščč¼F>|™0~¬Ü  āxå6™Cˆ#q!Ž@ Ä„8G d _¶;::(§U6‹åd°Ći§²²Rž¶ąöĒćń×ėMKØQ6Dɶ@\5`$[œįŗPŃh”Ņj–ĘēóÉÓ¼7ˆ“mĢĀ”––R ¤$­ņż‘u1•Ļļ—'&ŁÄN >fĢu¤‹"ć(0ā©Ī«W%Īܹ1NIš–FGaQŃ-'M»aåΜ=—u}Ō …zT;ŪžćX攌؛n’Nmd2NAžēʍ*ĘÉÄŗuuuŻņqī|„‘ž{žG®BļF:ŌŻ·’ 9zBµåÖŹ %C›j#ŌżōEŖßžRŚc{ķõ݉æĶkl6Üźä@¦čķķUs@NÄĪ Ģ„h¹²6'ß<„¾Cų×>ņPFõĖĖŹhƓ_„źŖ*U’fćńu_¤@ =KŽŽāģ¢U+WFq˜NqR®&L‡X€ņņ2%\°”V+€PL¢#ĒŽ+r­ZłU.OuU%­}tś„Všs«“\Ɲæn4ź4B Å $„’››¾£ŚŸ;g6=ųĄź¤ń ;¬ßÜ9µŖ]sp¼Ē©ŗŗRc‰Pē#Ó¦&‘ õŁśĮŅšųŽ;Aõ ¦Å˜06”EŸh׃ėD߂<œĒÉŅ0)ęĪ®5ŽŁJąO67+၀r,±ļO@ØP(¤, ČöÜó/h±C()–i ¬L&CĘų e@0ŻśÕ7ģ0ˆQ„H‹auĖTŒÓŽ©śC?Ź‚z[ Ś@*&”—AVœć¶ė·ļP})ĖiŌĮ$ŹSāä x˜PO"Č m‹ļ Dz„Kԁó°0ŖžƒpA(A“ÅA?»ƒ‹-HhzÕ­J2m³Ū—*i«ˆń.[ś)UOw!/œŸ°,lµŲš°D?Ljżŗ$ŗĶ®Z~[›JCXp`¾I‘ÉŠü ‰²0©`p|łńõJ!šgζ8&ŲM[ūȚ„{–°Z¦1P’]Ćx@<˜Ką:˜Xܧ:oX>‡ž” bq†@¹e†pCĆEĮńķgžR‚ K‹2Į",»gI¢„ ń…›%€ eABŻ2Į5drĀ…K&Im‚< īpÓäVKfo§$Ü8ō©®’Ųq|³8łbmĖ@˜¬A°r± ĮQī”įV\ »9vvėœ€rˆ`” ˆzYhuN=CūĆzq|d&R±žzŅ!sėR„ŚD Ū­ O®Wće·Q¹¤ebqõ^µ)ŖÉø"¶ōõõŅLœ8Qž¶ 'hł×…ŪgqŚ.P’©æS’ŁÓ4ŠŽNю+ Š £ ?æ Ŗ† *kČ?m:łgĻ“'&ø3]µ˜įz„ī„š±æĘ‰ā`m0Ē RÄ UäĢi ļߣˆTøšćT|Ļrņ–UŒH7P ÄÉX {w’Žz÷ļ„XO(AŽD;Śæ¤7ÓI4źļ1Ž?PńŅ{©ų¾ūÉq#‹8~æ?­E—‘‹’¦`ĆVŠ^ŗH…8QčĀŽŅ$“F£”N“Ćū©·é(^÷ł¦ĻŅFq°mZß:m‡š‘CŚŁ ¬…׎CĢJĢįtlčÉ®>ŗń½§)š…µTüéūr~cx­OP"“…LgҐƜĶāEóó>ӄ,!2~H1ėin'šµqŚ ˜M·Mn×\e1˜ZO·n¾į¦Ķć€4ĮśĶŠ4 RĬ~X–¤Ńʆ^ž9·ž8§cGŚėʐśåå5 ŅĮ8o.÷oQ)h}V>_!Ż·’€ė m+!P>Õ¢Q\?īC:ĄżCŗ‘õo^±ķֈ÷Dr€IcoalÜ“”¤q*k~>@Փ©xłŹaZŠ`ņ“5"kmTØ'4ā4¤¹ŌēxBŪ³‚ĄµBóėó9ø¹^̉{gŽÓõj :Ł|JõÅ+¶aĮFĀ\QΉ3p±Õp϶gNWҤއB/דoŅSĘ<éh[€°®µ1łiē¾@kņŖe®Į€0€x<Ė‰GŌEYŌa÷…ė &b±h“]A}›ƒ^‡ė™ėė:“Ę  āč¤HhCx™8aUb'»:ųćŃ}ā r!µU˼\Ö2™8S“¬‰¹“čTBčX0ŁõBœĆŹ}YŹ@©č«ŖŃ§>6^żmķVīī‘N0¾n¾Ą/źfŪ?V[`Õ7ʁ²¼ŽepŸŠ¶µŽukEŽÅ8æ|ē0mØ :Ē1v±N, Ņ8X3ÄS=æŻ™}B Ædā* ,C­œŽæ MŪÓ£«Ŗ™˜[ź^ˆot«Tī’ua(Ścįiø–ķ }>AhZ”Åļ(ĒAøuœč ÖÅ“2ĶźķqL\Źc[bCAµŸGŪ¦€æŃ_ æu źBØ16(,;bė«ŹŸÜŹYūĒ3āóLVLÓc0ļ÷™ėĶ„9³8Żż=“åÜ©+ą£Ę)£iuK—»…‰óÖ”hŽ|ņϘEåļ3ĻGƒTŠółƒMä‰\q±:vmČ*Šś*čVBĆDÜc3øź{\Ų½AL”֝Å7Āįcӛņõ殎)(kT›8Pķqø‹¬y]é¶ØƒĒ‚ö 4vZŸ]+Īl4 ®DłxŪ„ŠG —‡…¤¹H»ފ€ļn(ŒĶjušśĄohļ[¶hģŽ‚$(ĻķØ®ń­¬hŲ±5µŽ@|®Ļ“ņ+ņ8Ƶ5QWæ©efŒ£å­7hTŌÕ½*½’óTbōV׏“:£=J…×wSŃÕFņd2Ūņhxl-OĮµW)X¾.ćkŠŻ™tor*óÆ’Ž‡Z=§*éwōÉż[ŻDÄ<ö²3AxĪ ‚Z%t\15že%X+#.ą¦L T÷L·lśŲģīˆ€ƒ³jn™4ŒÅīx|ŗwĄn¦™NO^=žė}D9#ĪļŪŽ`‡‰ŗżŖ«O›Śm ¢Œ{ś»ä«™LWŗ‰’ģ”ӗ=t%n¤J‹ˆęMŠŃ'¦ÅhĘÄ4P2“ŠßŻLįVgK£÷‡0–uY]ǰVÉŁ!»óé’ģāŶ!q•¦å&–OPŸŠøzŠĪ–ĀIČŁba‡«żłŁ‰qY3nÖ¶­J†ėńŲø\(lŖÓßs [ē,gKҽqSL2Žõ1å:S—“ēRؓŽīŗ¤HĆŲ3iµŒ+“cĘ~ķ)EšæœõŠcæņŖĻ+šg 2ĪmzŻK;Žzi põVĒq¾ó¶ŒM·Ŗp™xė'&x®H'~gėkƌżóXpŽNŃpÖ’ß’Ąķ=ńõo$½Õ'o,NSĒyW‹”ÕłÉ”’%k]Ć=óOŸ„ȲŰ4sĻæķž‡Ē8焇M¢š„ÕTÜŽ"Ķ·:½oQąī¬¬y“8»†„5p6‚ceŃ“,4$ū噎cį×X邼dŃæß ™„µńÕƒ K¦ˆbÜ ~ĒEz_č_Ÿ\ÅõX]X“… ^/«ļ+27V&ÜH÷NżsLcw?øėż°Ę{ĆEÖūqšn“H$¢¾o|s§ćŲkųMtÆļ0™*¶½B”‚RśŠai‚awŅčįŃ3ŸŅ̉1uįK䍼ėJ R<“ŗļŚ”ų{üųń_#„œNøJ ŚĢ¹°čē0IjżŻ:ļć4£ /[<«{ę/}EÅPs-zœ_Ɵź<ωą°•ÓĒ”§¶ęq¬×nm+Łõ£Ä8Üś·»7vcĪÖµvöćä„8ŪLo“Ÿ·%ĮūƒjŲwY% =ƒ›ÖŲä”ßüĶ“aų÷OńĪŗ%Qeq Æļr'¶%nݵš—†EĄ‰89‰qM‹i”Œ.Ź’-õŃ«SĒØ³ˆk`yN_¦ŒH£ÜĮV“h‘’™)­ Ē9ĮĶBÖÄŃ’‹Ž·Æ_²×ć/Ü6c¬"£µĆ3„`)ęE)ŲĖ#.µłÕ}r4’ND ø%Äa7M'ˆõŠQW;Į1#K‘ ×-5©Võ÷÷Ė“äq°‡Éswł‡SvŒ „NV«35e±„…q³2ś‰ŠŃqĢ)1ąņ:]łO©¹FÖ…`oŗéīī¦ho?yśR™ŚŅ÷Ķ …č£wyčŸ¼ ŁOµGœ˜b-ŠRqē)ŠöDČ3ų5ź+S Œ¢¢"*//—'-Č)²ĪŖ w*r–U$ĘB@ˆ#q!Ž@ ÄīP WÆĖ2$֎Īkr'‚ šŪ«>!kø IEND®B`‚gnome-shell-extension-gsconnect-50/data/images/enter-keyboard-shortcut.svg000066400000000000000000000433331421543444100272000ustar00rootroot00000000000000 image/svg+xml gnome-shell-extension-gsconnect-50/data/images/firefox-badge.png000066400000000000000000000136561421543444100251100ustar00rootroot00000000000000‰PNG  IHDR¬<ūWQ›bKGD’’’ ½§“ pHYs  šœtIMEį 2×ßg;IDATxŚķ{\Õ}ē?ēÜGwO÷ō¼_i¤‘4HčHƒ„ˆ0‰ k­1‡Ź:֚Ąś ŽRI¶¶āJ‘*{Uq“ń©,®ÄĀ»^¼`³,•1ą%Ę+ „$$$ž3i¤ŃHóž~Ü×9łćv÷tf¤™ƒdśWuźŽ~ݾēŽO’Ī÷üĪļœ†²•ķ:2Qü ś©#_¶·‰ņå)Ū§hcĄąo‡_öņeĄV?uäŪĄ_–ÆSŁ®AūĪŠć˾U¶ś©#’ x©|]Źv Ūę”Ē—½$sž“|=ŹvŪV€<°kŹ×£l×ø­)¶ņz8ćłgxdŽ D¤S¾}Ÿ=«0Ɨ³H‡?Zš2›[ßäB¶ž_\\_¾…ŸA“×ˉ~”žM¾²ąZ“ ¾¾šETt—ļ^ŲOŽ“(_ <ƒĄ3 <›Ą·„%IŅę÷ęžœŚ˜‰|aĪ9žżāghˆ\*:.ßĶĻ€}ģ’@(…ŌŠĄ˜śŠŚÕiƒ kø6JEP† † †[­–0\64ģb}ća„\B`˜słƒ…GČØóŻ£’†1'ĪĶį=–³|WĖĄN×]j6ŁĖ™šz¶¶_žz*@]Ōxƒ&N&Š2c± jm‚4 CĆˆ¤Ę“ߦ*Ŗ+÷«ŲÖķ8ˆęyŽæĢŽĮßō?Į ѧĒYD üĄĄ ,Šc ]v)“ŗčZ`U¶ņ•…Ų5¶—_ ī#×ķÉ5l^RM{m“š\ē@–ļķéc{wŠ_~±•ĶńĖ>w’³'yåįES÷żŽæõŅx,yŖćä­ęū0ųŲüŻ®^ž<4Xņڳw·psKœ%Ϝ`K[œm÷¶]öł×Žńš›ēĖĄD‡Śį~Ŗdš&cŒˆį"Lf¦S!Ķ€Øåµ=°į >Ą‘ģc¼‰×€¹2 œN·Ņ; zhšąYóūł­dAą‘y»90ŗŒQ5ū“Žm·Ö³euĻīgõO; Ļ’ćŗFuUj¾’Aa’č#‹9p>5%4Åš8[»’Ł“ģvĖv2«ĪŒŃī Ńhd°DPŅĖ/3×ū7Ćē}ĻĘI%ƽdŲ<¬-tjŁ}iJyœķ:yXüīœć|”ńŻY×iK[œ-«ų»]½<ŗ³ÆäµGwöńā…L¹}¾µ<Ÿß{›•ƒ=H”"×a2&•ƒ6,‰¬Ė©Ų,_§Mt“d[gAĀHš%¾Š²ćÜ=\ŹŌBŠ AOØk'…U ÷“¶äĮ9ūØ7fUÆ»ęĘI¹ĮeĶoŁ®sIpēń£Ļœß0ćzuŌE95Ö{7vT3ŲQ=„>ż8ķ›k›łęŚęĖžļóJOŌĻ[_ķf{wź³ l"ęwß;@}jij„Š1V=!T•{¬BCƒ¢–›ĒжĪņÜš¤üXaO†~pņVWfMķ!P'sµ—C«Ēį­0īoÜĻ+neD}ō)jGYLcĀŗ ŹO²Cs„NWYĆ^Ån9Õ͚“'ä@Ķ÷š„„mn_zZ=ž­ą¢WĆkw‘ö¢ąKš$øØ(ļ ŽÉw=ĪÉįyą{ ¼ŽœTn›Æ7` ?`ͱ.*,"GČĒOE1“ŗ4¶š/>HÖ0Äyyd=vś/xŗ÷a“ųzX×ā<{ęžzßV>hoœćąvƒņ.6Kµ‘ęó5‡3Ŗß[gS4&,¶v$Ė”ü&H‚ʁaõ\@‹)Ž ōp‹_ҐtqŠ—†īāŁ‘¼”×3h7C,¾ dX#Ģā’I~tz ©tœoÆųÆÜŲx üS`dĄœDĘ;_EZvuü #ĆH0}Y°½;ÅĆŻ£üÕŻ­“%m¶ī½T¦åzÖr}"®‡œ* U‹\™źš×­eüÅ„opĘj'ˆ%Āč€ėēĀ[9Ł tØW•!ATņB÷ƒĢĻvńŸnł¢ ųgĮwBhEEIÄ@hMGōmv‡23Ó±›^=˶[ėŁtC5[V7žO¹Ļ˜²Óu-hȉ®ĻüĄå$²^‰ėŌ[4j•Ū×*LĄ— 4RkØ ıd€v]TžXZC Į””g5d­Čå'6ńPĻ+ܑ>ˆhü‹ą¹`µ‚ˆ—„ø*„Ćr£“S4“¦bFõÜŗ÷ҽėt£3˜ ¬āˆ©Žæ½;Åö)Ž÷™ö°õĆi*Ē“hZµżE)48®ÉH6Š×/č“ öČvš96< ° ƒšžÖ:‚fźP ˜2L=̧!†½;zǚų’īĶÜ>ö>⬆€aškˆœšņGńjQžÄŖ–ɏżÜ‰:s™lqŪą[«ė®m ąDmŗęŌā* ?We¢r^6Æa)dżI|7Š;V‰7–ĄK%RQTÖ$Č(G¢rā¼¼gOż|=ļ‹óˆ:©PĖfĒ0Ó—ŲøäVĶ=p'ģeŠjš\†\›ŻĪĶh1ó9^Å@ģ?z›Ό{uóg§c‹P™Ž­čĢX¦k7TYWüīb;32®Ń§ŠVŌG ¾·§Æ¤³6=ż©H¤äd[=)#¤CO«M|e`*©¤'–_ |ųAœtŖßŲ± FÜEĘTŠŌ3Ńӟ°Ą©uœ®Æ”śā(^`į–““‡”$Ņ—ąą)„+1-Ʀ/+łYēRål^Ņ*žjĮķū.:`ßXÓČŪ;ŗÆ]`µi²wå\Öč¢"“Å ¬ĄĘ¦ņ1¤Bx įH0ŒĀÜ.ihī›ÓEmŌåõīōµ’öĢ2ßC ?køJL4–į–„'ø÷öż¬]֍įyŃLNÆRkīÉžōmœ5ĻŖ~÷NšXW²Æ-NĪhź«7wTņ¹éŲÄĪÓ'Bšvõ¤ Žv2°śĘ¼BkńĄŅźI]We—Ō9ÆŁÆdß;<ȦŖiLX4&,]^}ķ pja=o/›KĖ;CŲ~KzX2VŖ!ĀÓ§hB¢ŌH”¹½¾‡z94ŲÄ”žfŗ‡jéĻTą)ĆØI¦i›ÓĒMŻÜÜq–dÜ $aóīē¼+EÖ/Ś?ZÉÅŹؘ¹wŻŅŸRĆMf÷“WN Ų­I¾zS]‰—|ńčÕg0lnŠń[źJFØ^;>TšźW“$;Ž <įŹę8;ī[‡ĶŸWqóšżjzų™÷ū ĆÖłŪ5 ¬š«Ūڹéųy– ;X†‹D0…Ź } n.+Ē»…€Ŗ˜ĻĶŻÜŽÖE֐¤xR`F}āq—XÜÅ“r‰ąćSm4„ń×"`µ}²ŠT’®¶Ęńüƒ™t¶ŠĀUSå ē¬lŽO) ®”W°«{tJЧšp˜o®’Ćī¾i×§ŲęeĢ+mSG8ž?Ü?ķųņ“‡/[läš k•xŁE ¼q{ĆÄqüŽĮQܥʬ06ė-.fX2a1I"h2³ĢŃIQ)LO‡ ęĆWNN·:9o…±\Š>ē#vĶ_ĢžŲŅYÕ©8\õĻ]£“¾ēé#%“ā±éŲkLJ&M ¹š½ß›āßī螑fīĢįg¦ŪÕ=zŁZ WżAģé擰gm-Óā—k2ļĀ÷ļ’Sz2Ą2gӂFø¹ÉŠÅoāā¹õšr [ŖŠaC»N<yhĆدК sl~\{ż²~ĘÕŁŚ‘,iŸ;12%;OzŹTĀUF¬śĘV`…”[ŚŹ’¼!ø«ódčE~ńйo”N˜#r™]%RĄSį¶X˜Ŗ$ŹP˜ż˜› žŃæˆÆęéŚĶ¼Q}'˜eXĖĄN ZƒKęņOQ‹”·"ÜwäC„7¾Ś F  …‡Q4;”ą]} ^®—ÅėrĮŖļYģ‰ßĄ µx±žnśā-ˆYDŹö6ļi{4óOÕwŅŻTĶę}Y:Ü[˜ū• X:„VŖ\gÉĖĮjĮj^.4ql.ÕšóŗĻńż%_äDķ0-ŹØ–“B®Iņā=·r¬­M{ßē·O„É…9`ʐh „B/ėID ¬!°ŚŠxB3čš ¤c¤½ v5ÆęGKīēDćāņ,ū1™eóĮ’…œjmbēńElųšCīč9ÅüL?1!–įaj?L˜ TØas°Š"ļŖ˜J’­¬ęõ¶ŪųÉāßįd]{Ł«–żų%‚›¬d×Ŗå¼»d z{Ysś·œ?ĶŅĮó4g‡Hź QÅVfn!7åC`ø–d "ĘњŽnZĢ›sWqøa+ĆZö× ®Ÿˆs|ŃBŽ/hć§Y‡ŗŃašFh¢)=LŅMcj%Į±l*œOŌŠ[YĖłd=éXmZå;WöW0-¼„Eo"Įłę9¹µŒ·ć„ēd¹ē_¶`GÉżż§paśJłÆŠŹ6©ĮxņĖ»åėQ¶kÜö»­|=ŹvŪ¶°C/{ ųNłš”ķµļä- U?ud3š'Ąē€xł:•ķS“š°-kŁŹvŻŁæ@łœZœ‰IEND®B`‚gnome-shell-extension-gsconnect-50/data/meson.build000066400000000000000000000040041421543444100225600ustar00rootroot00000000000000subdir('firewalld') subdir('metainfo') subdir('webextension') # metadata.json configure_file( input: 'metadata.json.in', output: 'metadata.json', configuration: extconfig, install_dir: extdatadir ) # config.js configure_file( input: 'config.js.in', output: 'config.js', configuration: extconfig, install_dir: extdatadir ) # Desktop Entry desktop_file = configure_file( input: app_id + '.desktop.in', output: app_id + '.desktop', configuration: extconfig, install_dir: join_paths(datadir, 'applications') ) desktop_prefs_file = configure_file( input: app_id + '.Preferences.desktop.in', output: app_id + '.Preferences.desktop', configuration: extconfig, install_dir: join_paths(datadir, 'applications') ) desktop_utils = find_program('desktop-file-validate', required: false) if desktop_utils.found() test('Validate desktop file', desktop_utils, args: [desktop_file], suite: 'data', ) test('Validate desktop file (Preferences)', desktop_utils, args: [desktop_prefs_file], suite: 'data', ) endif # Application Icon install_data([ join_paths('icons', app_id + '.svg'), join_paths('icons', app_id + '-symbolic.svg')], install_dir: join_paths(datadir, 'icons', 'hicolor', 'scalable', 'apps') ) # DBus Service dbus = dependency('dbus-1', required: false) if get_option('session_bus_services_dir') != '' dbus_dir = get_option('session_bus_services_dir') elif dbus.found() dbus_dir = dbus.get_pkgconfig_variable('session_bus_services_dir') else dbus_dir = join_paths(datadir, 'dbus-1', 'services') endif dbus_service_file = configure_file( input: app_id + '.service.in', output: app_id + '.service', configuration: extconfig, install_dir: dbus_dir ) # GSettings install_data( app_id + '.gschema.xml', install_dir: gschemadir ) # GResource gnome.compile_resources( app_id, app_id + '.gresource.xml', gresource_bundle: true, install: true, install_dir: extdatadir, dependencies: [ desktop_file, desktop_prefs_file, dbus_service_file ] ) gnome-shell-extension-gsconnect-50/data/metadata.json.in000066400000000000000000000012451421543444100235020ustar00rootroot00000000000000{ "uuid": "gsconnect@andyholmes.github.io", "name": "GSConnect", "description": "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. It does not rely on the KDE Connect desktop application and will not work with it installed.\n\nKDE Connect allows devices to securely share content like notifications or files and other features like SMS messaging and remote control. The KDE Connect team has applications for Linux, BSD, Android, Sailfish and Windows.\n\nPlease report issues on Github!", "version": @PACKAGE_VERSION@, "shell-version": [ "42" ], "url": "@PACKAGE_URL@/wiki" } gnome-shell-extension-gsconnect-50/data/metainfo/000077500000000000000000000000001421543444100222225ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/data/metainfo/image-01.png000066400000000000000000007275331421543444100242510ustar00rootroot00000000000000‰PNG  IHDR€8čÓĮC—zTXtRaw profile type exifxŚ­›gr%9v…’cZ¼Yl„v åė;Č¤ķźž…X]Eņ™|Čk޹@›ż?’}ĢńU\®&¦RsĖŁņ[l¾óCµĻ×óŻŁx’żxؼžx܄zµÖóPą{xžČūłī:§Æ7”ų>>~>nŹ|~šõ½Šū¾_AŸ¬Ÿß×Õ÷BĮ?»÷wÓŽ÷õųķvŽæ~¾—}/žū÷XĘJ\/xćwpĮņoÖ§Vjčüėł×‡ ݟSüBżsģĢēæ‚÷±Ņß±³ż}Eų cóū‚ü+Fļć.żzü悊Š÷¹żĻ'vwŻ~’ś»sV=g?w×c&Rټ7õĀū/„2Ü·ežž&~.÷OćOå'[dsšgל'ŚĒE·\wĒķū}ŗÉ£ß¾šŻūéĆ}¬†ā›Ÿ7)QÜń%“°(Hr2ÉZąa’¹w?·ŻĻ›®ņÉĖńJ︘»yüõĒüéĮ’ĖŸĻ £ŅuNĮlO¬X—WM³ eN’ņ*āĪÓtć{’˜Ļ“~})± ¦ęŹu»Ļ%Fr_µnžÆK6š·e]YļŸXŒ dĄf’ĖĪļ‹sı’ŸĪŹ}ˆ~—’_ĪrB&9Õė³yOq÷µ>łēa …D¤C!5-t’c¢~J¬ŌPO!E“RŹ©¤šZź9ä˜SιdaT/”ĒJ.„ŌŅJÆ”Ęšj®„ÖŚjo¾ ,µÜŠiµµÖ;ڹtēŻWō>ü#Ž4ņ(£Ž6ś¤|fœięYfmöåWX“’Ź«˜UW[}»M)ķøÓĪ»ģŗŪī‡Z;įēN>åŌÓN’ĢŚ›ÕŸYsæ2÷ĻYso֔±x_W¾²ĘĆ„|\Ā N’rFĘ|td¼(“WĪlu1zeN9³ Š É“5—”œå”12·óéøĻÜ}eīófRüņę’.sF©ū’ȜQźŽĢż5oČŚź—QĀMŗP1µįl;y[p®ķä<A敹y1&ļ óš ćÄŽĮÖēqŚń±Œqj”:ŻŲ>l› 7fĒš­œ\Ą>öÄpZrgµIöòg'āU»ŸÕóńgĻōʶk1‡ –QĪ.äģ}/S³iöō^ƒ„ß«Äß’V÷ux;Z4ćĒUźOįCʈō~tås©ļ5‚OĆ'V—Ė"² ĢžŁč)ÆūŁõ¤6 ķ Ś¤–ļ{&“7š>é:aĢĮzj&Ś2Īv&ĪŠKŚ6.]†‚ZcQ F[9®@XØgu=kYQĒ^õ¹åI`ų>¶ÉĆS¼vļ4xć 29Z‹8zQź}-ŠĖā©•½v¦„Ī>­”ģA—5½mīÅg÷mģÜĻXō„ƒötĮīż˹æµ)BY»MēF‘tzRĆ/{·cężŌަ8­}byæÅµB –ČōŖEĶĻÜi KDz–oąŹeR>Ąģś~Ū9»ÉDZyųYā¬k¢Bf"? e¦źƒšĪÉ&Ó\«yØwÕŲ›ū?ŲžQ‹ß^gžT‰óĄŹ=Tbb]uīP„Ņ vÕ÷©pōrzsD<Š{Ļ7w””Ҭ@ E±c=ćĢöZq‡…n˜”ޤŖüŖ½l*ē©zxsBÜx2Ö>łæ4ļāܔsœ½…®%•‰@ꬮ”Kõe×gkĄāŽa5‘„ÕRz^|E% tE¬5å*• <»Š‚ͱŗuźŽ+Žf—³TĮsŠ•īļ=ĢRCž€ž Õń·ä°†;ŒpĘÅ„…-.L’XՁW°ģ¬P£»„°ĻܽŒ[hi7xxR:Ś\čTßgHs9’Öw>~ēŠÜøķpÕ8Ił…^‚”—.č¹Eé*x¼‡Ņ­:hZjHŻ{¦nøü±~R5Øć¹J&¹€č½½­ĖQs—é7Dš¬€#Še»”ĒN xBKvŁ.¤ĶH†ĻBe Ź|ÜCQ›÷›R­»øĘŜR‚īō‡aKoŖ„ŗ œ~h°a.ČŲ z‘÷™ˆ@nLn•HÜČhYą“9Źd““3"č-{mŗ‹ĘdM'u2„šƒ9ń"N· ĢŁÄ{;‚ˆZķ| ®īTż‰³Ž oCqf.ŹĖB^÷"$µė;•ĮŽ*LŖ‘,€.yĆe8p¾rć—Weq ²y2‘tw)?B=ō9­N„&š»UFÖŹp—I—`ą³¢G䝣.öŌŃk†}½1ķ~ÖJēf=‹W­ØU±›éŃ5Ÿ.Į¤Ķ>Š$:ū®rIß]ūycSųŚ1›<“żüžŠób§ŗvƍµ&8e©=”,īŲ÷„$ŪkĆóäIA–ZBuĢĄŠ¼¶<ņŠĮ'Ø —3ĘZEćk-éaŖ¤ģRÆģ£ō”°×ČØŌȀ2|jō ĻȐEP^$(#ž(2”įŠX½‹z!õ'@Ę`Źe·Ē…Ś i‚-“5ĘIG(%:)ŠŅS$ m “r½č(ŒńpcTī‰%…¬į€d j·¬ ˜rQŗŠŃü0„ŌF}Ņtʶ >%ó43*`»…¦ŽĶ'ģWIž&˜–čJC¹Ei¢ūoA’ÓĢō>½$¼E+$šrfT%pl#‹“pqP I ~Ź"pBäÉčÅ“×FnüIĀ'µ§é|÷ČZĄĘĻćFiŽk5vy…“ ›Ž;EęsÕ„8µ‘tŸ6†±õiŗÓ`eų”Äņ„Ē™wR¼P5qėK’*B9Õm+-½éF0CÜŠŌżhĖ…āŽÜBV, ˜ļZeÓįČ ;!ż ^zVB³E©8üöĒā]1ĪLš˜E.¢³;%CÕN6ŽƒO“ #V ĒĄ ¢Ņ āt“Ä OƒŃņ¤ 7u ļ±3ĄĮå±JļZ2Bƒ‚ōFBc(2DĮņIÜ"b¶Œ iQŚrU|:t5Ž‚Čč¢Ęź¢, ƒŠŅ”‚f&1FWŽÜFFfIņ¼ź « ×Eeąč®Ó|&>hqŅšä|-'QTƒ  ĀŹ™"šƒ"C|MķĀäJ¬v·¶­±Į‚”<~qj|RwT*f—Ų5éŹ7P¹Ż®ćPš#aØņ¼ˆHå*9>Ų4„ŠOöēwóū({µ÷Bnr‚²ϬĻō©ŸĄjFM4!?ŠHWf=“Ü«Ä)š±w±Č÷śSćY©TnoŲŽFS-€]ˆŻÉIz·5Š¬ČŽjw7‚*E[ …0"·!…—åC mµ-Ą×?PIļ°źF°ŸD䧅n8©’3»póP*˜Ś»ž¾’3ÜiįĘĶą«œģ8FD\ņ²M¢Tˆ<-{E Ėā> “H@p®FL †GfqVŗ;‘nŹĻĒ|°;øĒƽš:*€`ł”>»Izš)üG£O…ĄJĢf 2뉕(¹¾[«]·x–bŃZTöā†xŽ”übÕéˆo„ņvtų¦€įČlĶĢūeō)špņ{“~Mr –3Ļć֗^$WŪ,o” T;‰jn€©$öŻkž$æRD¶&,€!­Zp XźŌøƒ:…Šxś…źjÜą«åą³,qĻ"’Ū4·[ŃT‚'pin j°BŃ)jam ßųōh,;ńӂ“µĪ €Ķ,ĶĄwąč@ŃX™*ų[u3Nؒ¾MŠer'–Żš‚•1tž¢ēÜŠ Dź„ż!į3ķŅŠi<ģõgD‘|s_2õųžX HŠTĮb“[S+åCQóD]uŠĖ'“Œ%ø4”4Čd„fzP–›hCóŠ~üpڲ&iJCĢʄ*±*ß*@šŌž>QUø:bš ž™Žm+gÖ ĖRHĖC°Że«\b`A£ā©mPVūBĖQQŅ0Ē…©ł;ĀUē˜OVl‹‘²īꅞī$fJ2°¶±xēūZbaĖĒ‹Ÿ–‚Šp7K0Š$üĄ¤ŽÖ-%K°QKPÆ/˜ ‹(€<Ę!Åh'.ŗŒ$g ­‹ ‰¢:éÄxÄĮ’#M°Dx÷ŖéćŖ{³ęÆwūófÉ×ć<鱊~ļ2’č|“ĻŅųņņņ"_Q¹€W“Šæ.®“÷P5zKŠć8Ü£›‘xū¶ČœžW2/¾¦€h8H^›wīkIQ…˜_×±ŽŒ±d#ņ$2Z½¹[Īlŗ~;6R+ZƒxāņØ,§ė ÖĮ”j˜Ā5(ģwŌ©GŲi€±½m =’ć(ĄxB0sń4¦ŪØ.Ž-'ü Āa6āĘT%™6˜`8!ū„­”äb·2Ā ī’d6S"ß)$“īÓgh ‚…)c­fsMwM‚Ÿp!ß`˜ŪÓ|'“šéĖ'V¶ßŅš^6ęELƒę-5?͆gLŁ%š]0āšhéłÉ"Šm½s¼OkĖēyāF±ņ1¤›ælĻĻŠ`µŗžÖÜļO1$Żx¼Pƒ¶ tžc®Y kĒ÷،ūbĀńÆ×Ö°1H(gōm”±>Ų…)įtŖeBĆ{DĪXÄ£=Ób¬H†5Ķ"‡5q18WZŪLuFC,~ø ųEćÜ4·Õ¤ÓŖTÓDė ś7‚”U =3[ØDS¹—ęz)¾OĶŖ©-¶MĆ”§T=賌¦{#ļ4 1oŻxoŚć׀{TŹÄøDZ‡µ©ÕАQįÖŪ.÷iē>Rńˆ»—{SZēM×l·BQ€{‰iJŒ÷ @@ŁōĢø,÷ŗńg£É‚«-+TęĀź ė?RHõŠśéw:;Qtt 4>£xš!fź—B4dёČĒ Ķ“źŹū¹~^¦[µ#¢É'÷ėS&OƱšEč’k>T ŁSj -ŸŚQXh½ŻātHVźĖ”‹éEÉ`<(R»- w`·5Ä6VĮxęфO ŪP©bČ…żD…›–ŃÕV]šWcķrŌśĢˌ£‘§­Kž–Õõ¤s™˜²š*ż„“d™ˆ°9WyśpO 68buŖĻS¬Ć¹c!ˆz€ÜąŃ–Ń~ąMƒTņŲÕ¤łŖ8D¶ĘąĀ‚Äf)=ڱ†ō¶X)ō[‘ųYŹp%юgĄuĄkRū|µ;q ¹” ®¦é)wƒRžż}¤īq4Ą8Rƒ_å ę‘J¬KSS„7 †\¤$“vźGÄACłEsŃy·| D{hĆbłĒoķ9u(ŻKÓfØzŁ*ƒF¦G€Y<ś1‘=’„ž ĮĮ}ųĆåH±“[ĒytPoįAOIŒ(C&oŠ¢Į»µMzĮZ˜ {C ×ÓPVö^{„˜‹&†q§\=|„2Jp°;”S0ŠĘĮ˜ų$¹ń /)¼ä(<ķj“ bpė‘­(=OfŽč«ßZ_gÕ;ˆ ®fūÓ >žŸM³Jާ'yŲĢt”8cĮyk:ü€`éZ!Ų ŖL>0Bņ9O9TD’£kKļMĀoz»ó’vŅ‹MpÉü®šV>BØ0!Ѷä÷„§Q½ēNŻ Ö•‘©!ā¶™Zkē⃔/QĄ­Ó§ ^T­Ŗ6fŖKHØĖ¤!‚ßZØ4-¼ń¦ŚcĀzįÄ&‚o„|Pœ,dTžŠŚŃ3HJģqžWÜÕŌ<4/ ØĪ4i `{R²J [wŪØ:.LQ NƒįG=Õež\^Éż«DS9·GœÓQÓ#S@¹\!¤¤ĒAZį CŠ<9ę=hA°0Š6ŃĆ“ĪļŠ—²ä¹wä1M'qT@Žā3ŚšM‘Į”é=¬†Ļź:ßqēL»ĀCÉWpDfā1 6‡•w(Š·k“keΊdŪCÓ!¤ń6–äČ¢vmC岆Øj€ēī®-_z,ļ,ŸÓ“- Ķqż”2Q”Ąö&/9ćNŗ–س,µ9Æ”QŒ–›€ ū5"®  .łINh–ŽŅ„ڌO·§‘.Qw‚a…k Žø!¾jTčŠö.ŻEl‡§=4l³F D]„”[nŗŌv;C<šJĻp8Wßp.¬^›sR4ß~AŌ) PĄĖŸļ0’Ę[Pynh¤®ć†šź‘f-ם²>ļ3ĻƒO wāvņ¦U¦Ś”¤į=DŲär]Äo%¤x ģQ[¢Cyżaŗ¶¢I°īšņ8%l"qPOšžķxvŁkāłL”T„WRųE{˲tӆEIA”¢Ét2Ąßamń8ę¶źLE”FķA&īĘyxK·ųł³‘B'óĪäDJ¦€ÉŪźē€ƒČŽ’'—‡ĘH;š,Ś=’š” ³ęĻGĪ„e@¼ŖM4øĆĀ‚·aĖL5ž€žāśKśŚyX!iĮ‚ J\Ąw²ĢĻ”I”Ŗ”ŽÓVōķŹ,Ž ačVēóiuU¤¾„'ˆĪHĢģńīr˜EUļĮ Ÿ’ŗ’ˆ„¹'-ÄE¹¾„"艊ģ“Ė­& ¹±ŻĆ§™¹kNMxŠ~¹IÆķ%ķ–H<ā7cPĆočw0×ip4ż^Ų\"¾×‚÷ kĒā‰eÓü°>iÜ«¶ātöŒ…ź,VJ½š5“ösSÜRÖĆÓKéYŒļ;~ SŠ|\nź,Y$ Qrō/YŪ„² SšÕtAŽ )KōމÄC[1M—„b äį”)XƍļHسĆ7gŅ<›"Ņž(o Óֆ­=`hø~4§¹9¼®ą‡¶Ŗ<łƒJš£ęj0y®X8’B®YSÆG+hŅ/śÆJbö„} ԟŚnŻ/@LsHäY;½Ÿ%ż‘ÜP!.½=_«‰FĖRbņŽbĪ64 ]ĻD‡B½‰¶Ź ×ÜJbp§mųB{S›8N­†(VzgmTŃČMē— Ź{ ”Œ„š(¼€¤€į¦Žą®ŃiĒēg8¦ķQóf8‰ÄźZA‡ˆ ŸƒbģĻ!‡„.čiÖ jм¤:ķ¼ĪFšņFJ~hŒDJÕ– ķēL¶óį’žeœ®oš`©„ääŃ[ē iƒŅ/÷_‚1ī¹ó0 µ¶åłµGLц‡^ø½sš¤j–¶5†C"Nŗżā .;v2 ²@Śt<4Ø3Æ(„£A–Ž  ‹Ž 0°Q˜Ų½øŠš€e$KĀģ ­ƒ{²o PŻ–×ʘ v }ącµkØóyšNÅ­“w®kƒ­GónC×Ü…Ņœ Ī:\ēå9hśƒJ-)Č vōµ68ö­s–¶‘:~ ’a øJ‡öźš€ŲEƤt裉-ČēL·uy`µ{Č’īc±Xü˜7aą ‹ü `ڹQ“ ķ¼²ÜFv~ĢŽŪŽų ŠJh: Ų4åFāŻ2ä0£{CŠŚļC½& y‡2äö£šä:…tfC[ ’ę_ŗżchōI°(1K^kdJŅDčŪɶšnPµ”CØŖ?Z~Ø£Żó+tzh”€s££WĶ.]Ē^ł.«­Ao˜¶å` +8Ÿ•ė(h2ˆq*‘ņõš–ĆƒVhšóĆDCSDąLh¼]$ ¼¤Ŗv4ŌšŚĖC„˜{īC't4]Bhį©rUŻ,š™š[±ĆRaƒfņņĻ>jéEŃ4¬“"ō”•¢FCŠįŌX4ūSž:Ŗ”±ŌĒ„ÉüķˆiiÄō ,Ī’r“#hū¦÷æä¾¹zßÕ8RʐI%M§ż’ĪģAl1nł7z_c„påÄÅuDfTP6QIuJ¼N V‚ö‰j¹'£6ˆ>ØsŠeĻ ØiK.é°Ė²(Åź°YOqRpøüśįӚę"¦Ą)kk-?™,TĆoząŖ„£µ'k6Ÿ§dž¹Ļ3õQŌž©ĻĒ<č÷ćWČr“ķīŁtÉ ł …•)ZżÜŲŽ»ü„„¦’)ąÕ|ˆ’„b›Ä'ixŁ‘ČĖ®ąv˜Ży/ˆĘ“Q% G<󷾓‡†ŗŹŒ°˜š Ūk….‘Ņ„”`%\·†‹HTōqұ‚ ­łXųš¢,Vx<ļŠ¹D=“–7Ō%ł·\ÕM‡ū})yYaæėcÓ.Ø=ĶĀ—'YV|:]HėMT-- Ł’t}«§µ­ą&=\hęG ļ’8ĆņxlæuLęžĀ›ļź ęę¹OŚB³3Yi·ü¬Hļ5t¦Ēé\ k8K ÷ˆi!ŠS§óÕæ®A†4ķ <§®J^‡¦ĪŠØ‰O½m¹}¦ź„¬»ē|vżų|Vk\Š7n ]īöŁ©ŹGCCG‚īŽ‰žĻ‘"™¤ń†ļkžXøģ7īļģį]’fß„%mr‚†ą¾QōNĄńźüĮ¬Ś&Jf/hØ­‡ėłĮłP4šW‘’šń©cg¤u_»Ųŗˆ»ĒnyXF“‚Œ€O£ßįĆNś’Šņš³…‚o8 [ŹQgÆņŽq.`é<°Ō3ö)ź¶łŒ2²rƒ™$0<ļķ¦ĻQ Š$襄æ}ē°BĉQ£±„¦„¢Ü~ųüŸŽ¦A¶zčŠÜĀ2œCqė4"˜Ķ°D|`|·®ÖŪW­?q“_óvVy+N‰Ó¦ßsˆyŸ®sžrÅ7/ČÄ~wĆ""Ižļ?Z”ł½Äg…“S­wÓ®”æ~ś×‡WMīāĶłXū³r֍‡īŚzNTCÉü}d‚­vŚ<ŹtzrlI‹H¶Ü±bŅU÷f½. f£”Ż=–˜t~āĒ D££›°I3’ 4Į]ØD->MbKGDDWh~Ŗ”© pHYs.#.#x„?vtIMEā 5&³“¹” IDATxŚģŻwœÕż’ńהŪļ.[`—^•*M‘"öÄ|ÕhԘÄ ֟-ĘŌŲc‹&vA°kb‹, MAA@„Hļ,ŪĖ-3óūćī.ŶīĀūłpEīœ{fęs¹ŸłĢ9Ēč=čhiņL…@DDDDDDDDDDDDDdļ °ˆˆˆˆˆˆˆˆˆˆˆˆˆČ^B`‘½„­ˆˆˆˆˆˆˆˆˆˆˆˆˆˆlĶĆ«ńu£Q· Ą"""""""""""""ŅdUjwUaÖó<:ulG‡¶­1Œ-ś4 œd’%ĖW²b嚭·5"*‹ˆˆˆˆˆˆˆˆˆˆˆˆ4Ažē„ŠŸžW‡w†Ń āejŸ›„z¤¬QłĻæīŖs­Ś×żzxxžĒQƇPPPȬ9óŖĪ°A1u]‡v­[āŗĪĻcktjߖeĖWbšV£,Ū ¾Ø*OŚ0R’Ŗo@«>4«y':¹?±xxŪ鯶mėҧˆˆˆˆˆˆģʛU7¶Lœ+“ö],’üFÄĻsÖŚ“©m’;z_Ս˜ź[!ŹIEDDDDdå=žėŃóĄ®Üü×+hÓŗUŽ?ōŲ“°,_ó±Ŗ‚k8äØįƒ9ø_öėŅ‘¬¬L `żĘ<-^Ā”é3łüĖiÄā‰ēAUēŚ¦UÜwæ;ćLÓJm«,֖——“ž”]›V¬X¹ŗ2Q£^ēvŌaƒq]×qš<&|†a˜•ūs9öØĆ1 ƒ#;Ó“˜šÅ”F—ėŁ tE·+yå”Ćčd&Ųōņłœś\1ŽeÕ"YŪ$`&ITÄI`‚ēkö;īzź Žt?ā”sā?Å”š? ھQĘ4 OŁ=7&<¤ŻšŽæ9sŽīJ×6YDāy¬[6—ÆŽĖcSĖqĶ]“,{xø^:ķ†ŸŹ¹æīN÷N-Ér׳jī§¼>öæ|°Ź \vŅf]× {Ģ…\ybośä–°zĪ'¼ōųųtS³ņ¦Ąę¶&Y§ü“—.̆·.åųoØUŽ,"""""²“ä'™ą¦kė^üH&☦]§°ēy†ĒŸNū-śżÉ¤E£?kÓ¾mŚ·mƑƇrYž9<’Ņküēķš ³^’VÕ$›g5恻FŅ¢y‰x Ÿ?€ēyLžčæüė©1¼ūŽGōļŪ›ömZ±zõZĻ«{Žåēŗ8ŽS9ņׄ“¬Ó²Ŗ Ī®ėRV^N8Ä©ģ¹.†iјŅ<»Ž•cŅip?ŚSNEŁŁYä“Čfõŗ õ+8».^eń÷ĆO># qĶ’»ˆcŽŽėø¼ūĮĒ<łģ8āń8æ:śp<7õ* čE½G;F/Žš†1ūŸ<–×v$ĆŪ½Ęܕ~°Ģźv[M©ežG2Ćqā±rĒaX„“ł÷¹šo Žā xøž[=%XÕŠkĻó0K'U· †Āxf-śŒDńj˜‚ ŖŸī®iZ²†No-"""""²ļŽ”p©h}Ęśń!~Ęę&Rk$Łį 2‚ął¼ź|/•ƒUo®L%·˜¶yŪ®+åyåÓĘq×Ķ‹˜ųĮ" ¤Õ•sžĒM}å·ƒžÅ;’KŸŗć6ļ}ąąYąųŪŅgxŚż4‘ʈū†sÖŁi^ü ·Ÿy ćWg2š–×{Śļ8’Ųטų†ƒa©§Ż†8•ž(gā:I\ĒĮš”ģRDDDDDšm„0­ §į†1M³†–•łVe­Ī¶}XVŻTĮ¹OÆlŲ°‘ėn¼™¼M›8óOäŅ‹/¤en.žē±fķ:¦N›Ī-#GÓ¶mīŗć6ö?'™Ą¬ålH©bs‚ɽYćöd2ĮWæÉõĶ”©Ó|č!œtĀqŒł ²²2ÉĢhĘźµėń Ƶ½TmņźĖGpņ‰’Wżź9gœĄæž|ŽśdvÕ5Ź­ÖM®iß4Ø6iÖēĄ<×%ŃuǓбš³·yõӣĬ® =“9¦ėVi“v;œs3=õļ½ö(cž>‚Ó;ńėūĒ0ŗ_Ļ7„KߘȔg~C_ƁĄŃÜšńdę¼s‡Z'>ü.“žwŻÆØr®m›.—½Īļ’“›ū ꆏ'U¶ułĶĻļ“Ļ!¾Tœh/Ž»ņļ<óü8>|éžøę0ś§Åp=Ļ 3ünōIŽśļxŽ{ž^y4ƒüńo6ˆˆˆˆˆˆČŽóG'šF¶é"=$mF$=“ß&i…0 “x«óxä7˜2ŗ;͜$®cŅźĻ/šé{cø{` ū7\÷Ö«¼:¢?æ¾ōļŒ}a<ļ»ƒ»NŹ&ĆuŖó5Ć0šĻā£IIF2ˆ¤e²×2iŚZĆ$č·ń<_ɎŪ8®ƒėŗŽü \}!7Žń[†PA|æ Ļp(ŸśļēE…f¾ł9K?ōĮu’•kP¹ų¹˜kĪåéG&°±z©aå•"""""² ņ-/UH¬ā… GÓ §5Ūź'M'Šņä£2kźDfM™ČŒ/>ä‘ūnĒ«\ć¶– }å°£~Eļ^½xóõWųż©æ£c‡ƒAB”;uäŒ?žĪ»o½A·®]vä±ō8tsŗ–ē–ˆĒ¶»=ÆąōSOā_? ĄĮżū²iÓ&¢‘pķĻk{ Ļó8bųPžpĘ9œö‡38žøcp]·Ī3;UåČ8å¾ųąu¾ųąķü¼ĪŁ<%5¹ž9¤Y÷+ \Ē¢ėaÓĪ[Ȥ)ė)ųjß&lŚBg/‘*žz8väżÜwzOŚ•Īā‹)‹)L·qņ*˜ūö‹L\8Ė™<ö_üūÕoY‘HVŽąõŖ?Ų>YBĢhKļŽQH&HxŻ8¤¹ęŽ™Ø©›Lę÷ĪK;ķÓu’$Œnœ|Ūh®?ō8“ś§ߎ$“‰ZJ«óØHĮpW_y9¦i2iņĪ:÷|ŹĄAC9ėÜó™4y –eń׿\E(’N0’†ķó×aśg'™ÜīöŖm;uä»yóČÉiAyy9x>ŸÆNē~ōńt?č`ęĢūžōfͰm;u õØz®Ėsć^ā”ž{»mžõä3<ńō˜ź<·>ź<“‡GŅ:ˆ£‡„a®śœIĖ‚ų™ŹÄ—rčGrxĖ·Y°Ī&Öód.8Č łÕœqń›,óüŲ>?–ķ'¾ģU&wG¶XĪŌńOńRi&`ørAåŌ ®ć°ś“ųö¢?spߎ4q*KŪ `X«k^ü˜å9¢j˜“ėņĆ»Æ2ń×§ī°Ļd2;ų\°ŒUĻ\ĮŸ^FÜhĪ¢sߥcś’e¹-hm:żų)o¾ō!óņĄ „„#ų,żDDDDDD¤. ĆĄ.ž˜»ÆM#pė9üŗSO†Ś“a§Œ xŽĖ†ķĒŽ—SXn@0ˆė:8ŽŸĪĮĢW¹öŁÅÄ;ųt!ˆˆˆˆˆČ/$5@Ą0-,ĖŽœ9Ij[%5 #µ†o „aō8ŒXE)æ:ę(®¾ņ’ѹs'š<,\ÄćO>ƅ—\N !MĆēV£ė2ē“·ƒM^õÆU}¦^«z½įƒ: Ąöł1- æā}yąžŪxó­wźßÆibš&ĻŽ}x¬‚뮽z«ķ<ś/žóįh³:0Ž’Y·K$UlMvĘÆr įĻ÷ÜĶC÷\ȱY \»;‡ NĆKÄÉźŅ‘+ÉO“æd­/pz”h:PåÅa¾@ˆ@0ŒeŁl9!–ašųÖNä³ č~q²śDW–0ń£E$+S®n[łäĄŽśt“I²Ū¶$lŲ“½ą?Ģłn&?Ģłū†øFŪJP1åMŽ\ī‘uĢH^ųšS>į.;<‡ˆa¢ēµEDDDDDź’0©|- ńćĖüå”ßpōy£yšÆXRę#­ē™ÜxÅŚ8‰ĶɹēżģImĻó6OńœˆįųBĆaÖ®\CÜ3šūķo\x®G2÷xn¹~0ķc³yīž·XbRkb;kcaš6>P8`8‚eŪÄ**š0‰¤E±* Īų¢¤=¼ņr\Ē!ŁńLn8ÉaŚcOšyYź†Į–ǤلDDDDDdO©Z¶Ąēó…ńłü?ŪV«Ļ00M Ū ‰Фóš÷ŃÆļAd4kFFFĄ³Oż»räoŪØ{ń·–,XHƞ=X³v€X,¾Kś7- „ķósóuWć÷ūyįŗyšŸcŁv “FjÖ*_€P$Źó/¾Ā=÷ż£zūƏ<Ź“Ļ>O0Å篌[=k“u\9żóĆ¦µe`äō깜­t6˜¶/}L±Ŗ-Ū>;UŒ †0LĻu1»śƒ¶m;õ¹¹Ąj¦ea³ž Ÿ|Ļż2 G3JéˆłĆ|ø8” le[Ć41°vŲ'€ė¹”—•ćį°ękøü„õø•Ēd™åäń9ŸpŪļæä„”Ēš‡ēWĒĒy·@ĪÕ#øwA:fŒŁ·†mū EҰ|16͟ĄS³Žå©ē/bükŠÆwz0‰«n:;Éø š‚ĢIÓJ•|+oZx[d€žė‘Hʕwü™_e®ę‹Ń×óÄ¢žčę$ŚuŻ“1ńcśU#č{ÄYX½ā,7̦ioń†·ó> ß’Ą§­:€čżS™ķ†*oHųšBųż6žx K¾x>~‘×–½Ę’.oMĒö&īVžPˆˆˆˆˆˆģ“¬\öŪÆœ5 ņ(¶}Ų¶Ÿ ’%ä'ˆĒ‰y@›Žt°æ"/ ×į10Źu­ ³Ę¼Õs=’Y‡sõ]—qZėÕLŗc×¾WŠMĆ“R¹¤ėŗ;icą¹IB]ūsH›li½ŲϜĮ‚yә\r'r2’—3Š×Géöė”`ÅY0ežē‘÷Ķ›¼üS<5Ņ×0š²śrĀQ]š-ś„w¾ĶcŁrĻs1Š:C"""""R[ŽŽu=Ļuq g«üiK®ėā¹n֖…mF‡\:˜[nøĻóøżīūłj֜ü5 ˶éŁwå„“ĢmĮ ćĘŠ”}ūTŠiūč5`n2É3Ož Ēqxåõ’Ҷm;0 ņņņSūŻEĻŪ†Éł]ė:„"éųƒa²ĄšVE`ĆąÅ׎ ’–?lpńźP®šž9Ń}Ē6w)’p*óüDżAń|9£”‹OķĆįCŅų×[ÆóĘŖœ}е<ś`o>śĮ¢y“ļ~ē—$XøxĪĄ>œv敄fĪfāės˜³E°ŖFū æąŻÆ/ā°_ŸČpo>ć>] ¾,ĢmfÆ6¼M;ļÓ4±¾{§fĮ­FščÓmycr“?ˆ%cøęÉ%tÆ]e³`ź|”4§’qmń%3w^1žd«GŹEDDDDDd§7$*öū£JŪĀå,^¶‘_öļ֒L»˜yćß`†canœÅ“egqp—óøėŃ˜U²żśE±jzŗw'ū‹gɵ÷]Ź)m<ܵkØčw ·œš)ŹŹ’ŒŪŸ™K"óh®½ļ²ķ·yvžēQųæŪ¹¢¼'­Čē ;9‘'Ÿł-‡_y—>ś–fr`æ,¼%ĻńĻw ńł¢Ló0“c©›/®C²Ēuzd'Ņf<Ė].ĆmFe*-"""""RÆ<Ėu’Ģœ2”śµYS&ÖŲvšQ'ąU|æžüńĻś©ÆŖ"°iŁüķ†kÉÉiĄßnø–“’x^ƒ¦}6 Ÿ?@¤Y&P„¢Ņ ĪžóžöI:thĻĄēˆĒø’ĮGØØˆ“••E"‘`CŽ& c× č4 “1O’ øäź±, ×qŲ§±ÕÓ@åRD»fŗģڟ½®ėŠsų@rĶręLŸ‹I#M'M' 0wśwć§ūš!ŲSųLjėxč£E“>’“Ž?˜ż.ų“f‚łĻŽęVčv<'’f(}2Ėj8q ŪĪēógSfŁX Žēƒ”ŌŠņ-N>5ÜÜaž³·m·O°,~{Æ_}WޙƒčPN=ėDŽźcC¹Mš›$ū‰ŁkšÓżØÓøąCč\<…WG]Ē£ B©‹EÅ_‘Z¦‘©‰Ķuūü§|µÖ"³sOśv°(śqćn=Ÿ’÷R®/€ĻųžēG>Ź›ó‹ńučM7s*’¾ū;¤f’Ś&¬Nō¶ü<×%Ž¢‡“LĶeµģϱ'üĒ÷+Ž;ö(~uHk‰ųNŪ8NĆ0š»k˜łæwxw”¾¾€ÉŹ—Æęœ{>ą«‚6ōģ’`Ł'råE3=Å  G‰6Ė"š‘E$=05µWÕTh¶?5Å“¦‘zåZžG2™ØUŪd"ŽLŸńõVÆOŸń5Ę6¹V}ø®“šżhóĮ„^k€TŻĻG !œÖŒHz&E„œ{ĮÅ,[¾bó¬¾Į}>Œ_yƒƒźa,Yŗ×õRēÖĄœĖ«>%·ś÷UEso|Ž›k”~lŪiķŗµ’ŽƒŽöj1Å©(+!‹aūż„©śŌ¶Ņā±r,Ū‡?ÄI&‰ĒŹqœ$¦abūC,Ū&« V^Šė:ųüA”0žėRQV „"iųüA'IEi1±ņ2LŪ&IĒ’ŒĒ(/-Nµ §ał|©>ĖJ¶ßg8-µļxŒxy)N2 F* ÷UVŲ]×!^^F2Ēu]LÓÄēV.øÄŌŠ""""""µL–SąŖü/«Ą©¼PkUD=×”¢¼4•':Njm* ÓÄ_¹¶T<^a©õ„-›XE9ńX9¶ķ'‰būüx®K¬¢Œ²āBā±rš¼Ź$ڬ|ŗ:ˆ?ĀuŹK‹·Ū&I«ĢwS7/ŖŽÅó<ā±ņŌłÄćxž‹eūĆ‚a,ŸÆņ&CjMb7™$VQF¼¢ Ėö GS`C¹„ˆˆˆˆˆŌėŗÄ+Ź(-. ¢¼Ļu+§<Ž\<4+s©p4 ƒņŹZ›ė:©5gCaĀŃfų”z×¾\×%VQŹA=ŗ1ź–Įóyū]Ģžæ@0²KjjÕĖü$ćÄĖĖp]—@(ŒĻĄu]œd×u8öČįäņ͜y˜–ŹńźXö<'™ąˆaƒš z4īƼ<¾žz&ō§yvvj¶ė`ŸNš†eūvYńvWØ}×qp’ÉŌÅaYŲ¶Æz”hÆ2Č©§¤Sss{©Ą;I؞܏iZø®S¹-՗eŪąQ½H“mū0-ĻsI&8ÉDźż¶òš§śé†Ŗ×RĒ·ć>«Žė8Iœd³ņxMÓĀ­üp«žX0LĖöUŸ«žŠ©[]•’9N×u1 :ײ¬T.ęyŽ“ĄI¤ņ1Ć0«o`T%īU£r«ņ3'™L=p\Ł—iYÕ {2Ēu’[=•mn±OĻsI&ŪmS•»n™[œOĒITę¶/•Ćn[Ųu]7uŽ•Ē^uœŹ-EDDDD¤!y–ć$HÄc©ĮŽ5MålX¶ķ `L¤jmžēUē&>ŸæAėĶzžK"§¢“˜Š²‚įh嵞]: ³ė¹øÉ$žē¦r/ÓŚ*ēt+kVUń·ūŽŖģy©ŁØ¶Ų– kź/õ"†a4ķpźd¼Ķ'Xyńl™“zžWłŒóSryŽęėĪ zČuu_Žę×· `U»ź>jz?õķsė¹Ķ«ƒöØŽ¾ķ6%č""""""õø)P•ƒķ$׌*§«N<·Ž¾UnW™ƒn›Ÿz^U¾XóMĆ0¶Č ·ßf{9ą¶ēc?Ļ·½‰°ķ±‹ˆˆˆˆˆ4(ĻŚQ޳9¹©Ī»R¹–Wė¼§¶¹^ÕŃ-mZ»iPeuNYCYµĪ±iš;ĢĻvS×uH&ā›×ų­Ś×–uĻ-r[ÓŚ<ųµ1€ķŗ46Ųń\ą5^(†AMo1¶øč¶ķcŪv5õQÓ±Ō­Oj>cóviøźl'¹Öörŗ­śŁYZõśĪļķ儻š|vvœ""""""õγźø~ļŽr­†äz†iaųL,Ÿė¼n7ę–5呞eԘ7Öq¦™Z^Čr·z}óŽ·y°Ł4S³%7²”ĻÖ©ĘšŠė®Ų’VÅdĖŚ£ūŽÕTaļXÖŌĒ("""""""""""""²wPXDDDDDDDDDDDDDd/±y hĆ M'IĆöł1 ՆEDDDDDDDDDDDDD#ĻsI&ā”—SVRžT€MĖ"+§5¶/ H‰ˆˆˆˆˆˆˆˆˆˆˆˆˆ4r†aāóńłƒ„"éäoXė8˜†Šæ""""""""""""""M”Ļ ³Ek0 ģp4]Å_i4<×aÅĀoh×µ/†i)(;įóGÓ1C‘4ECDDDDDDDDDDDDD×I°ō‡ąÄšœ –ž0×I(0µ §aŪ>æ"!""""xžKžŗåę­&V^‚ė8 ŠˆˆH#dZP”fŁmČĢm‡a˜ ŠˆˆˆČ^"™ˆ±üūų|&YYęšÓ¼©t<ą`lPAŚŪēĒ8ö÷{ …ˆˆˆˆģóÉE¼‚å?~“Œ G°lĆ0‘FČó<’ɱņ2L+@ūżūėF ˆˆˆČ^ +gé÷Ó …‚4Ėh¶Õ¶āĀ"JJĖčxą!ųƒakōx¤ˆˆˆˆģó<Ļeł_ciĶ2°}>EDD1Ć0šłüDÓ30 —å?ĪÄó\FDDD¤ ‹•³dŽŅ¢įŸҚ„“žeÉüiT”)`; °ˆˆˆˆģóņ×-ĒIĘ †# †ˆˆH Gp“1ņׯP0DDDDšØ²āM,?f͈¤„m·]$%3³˾ŸFiįFn;T‘}^įĘՄTüi²į0…W+""""MPqž:Vü8“ģ꣄#;æ? ‡Énт• æ”(oX€EDDDdŸ«(Ų-BDD¤‰²m±ņBDDD¤‰)ÜøŠ5K¾£ynPØÖļ ƒ“ČĶaķ²y𠦦ļĒ ˆˆˆˆģė\'‰ačŁH‘¦Ź0 \'©@ˆˆˆˆ4!yk–·f1-rs±ķŗ—,}~?9¹¹lXł#Éx-ŚvUP+©,"""""""""""""{Ģŗå?P”·šœÜ\,»žåJ˶ÉiŁ’ ėWā8 rŪˆaū||5ĢADDDDDDDDDDDDDv;ĻóX½x%›Ö“›Ó āoÓ²h‘›CiĮzV’4 IDATVŒ§¢H£GhŃ2w—ŽŌ5M“¹9lڰ‘ł3>hņ12 8œ6]IĖĢ©óūU‘½NqžzÖ,™CVVY ˆČ^Ī0 ²sZM‹½ā|båå¬Y2č]ē"° Ą"""""upį¹¢G÷nµj›H$3ž5ę}’£'"""""²‡­_¹€Ģ¬,”‚!"MN "++‹ «Ö¹¬5€EDDDDź ¶Å_ŸĻf’®4‘_@¼¼„ Šæ"Ņ„B!beÅu~Ÿ Ą"""""»Y$¦Ū~ÉĶi®`ˆˆˆˆˆˆģ!ZóWDöÕ’—©,""""²›Ł¶Ezz”vmZ+""""""""²[©,""""²§ ļ³ūńūż‡÷Ę_Č iNƒūsĀ'põ‹·p~›xŽ×ģŌĒy’Ö¾4ßęœ<³ æŗļU9¶öO§÷WøgH™®‘]@`‘&$ŁķpŽu§3£h(CŚ%5e‘=ź”{GqÅ%ēFŖ_‹„Ć\~ѹńa^īń$'Žõ]½Ū³ZŠķ¤ó9’ˆntI/fĆü‰¼ņģ›|¾¾ęźõŃw>Ā”³§RŚo8ŚXÄVLåϼͬĪāÜcŗ³vœ‚9oóĢ’cF”]›ö ųćyœ5¤#mķ ,Ÿö OŒ›Į‚r°ˆö=“8”~ķBųņ—³lŹī|q1ž®+‘=ķń§ĒsŁEēŅŗU—^xąŃŗU.›ņ xā™ ‘Ņ`‘&ĀķqG™“˜²Ą‡łżL¦CŚŪŻę~;śō,įŪ§ną’ĖFs÷üžüīŖ“t€(Ļ»“ŪśĪēż{®ąüėžāõ²¶“6SUйSēRŅ{ ż}•}¹ōÅĀisj<Ļņ·üńco•eDčxĪŻÜuš|tßU\pżXžėÄµ;ž¾~w;YJ;zXȬǯēā’w­:”3ļ¾kZ|Į+w^Ķł×æĄÄēqż]*3nēßĘ5Ķßå©ėFpęu/1”õ•ŒśSœģßréÕ=¾w׎ø„‹īy…צ­£HÅ_‘_DAa=1† 7ŃŗU­[å’_PČcOŒeS~$""""Ņ@*‹ˆˆˆˆŌA~AaŪįółvĮžĆtrs¾bVÜÄ*’Š?dŃgPw¢[µK°jŅŪ|¶ø¢‚„Ģ~å#¾ˆö£o›nōpN¶IO½Č'KŠ(\÷=“ǽĻŌdj$®1g2Ÿ›CŌ5@2c0CŚ}ɤYNGļõžxńy^ßāēń£ø¤S¬ŗ“v'¾žĻž|™‰ĖŠ(\3‹{w£§ņūĪvĪ5ĮŖÉļšł’BŠņ1ķµĻ™_Āōw§1o] %«¦šŅ»?aŲ=µģćųƐ¹¼7vó āÄ6ĪāĶ×¾”üąCSŪ3[ÓŽ^ϊ—±¶¤Œ‚åsłjq ®.g‘_LŅqH$’›ŸL’H&‘]@S@‹ˆˆˆˆŌĮƒ’|ŠV-s9žø#i×65ĀtĀg_²`įO?k›H&Y¶|%ūwėŅążŗ”AŽæ‚Ę,Ć RʏóVāœ<ˆCĆ?šq™UćūŒŠ b~2|l݉nĘ<Ž^Zs`–Oē‹Ł#Łæ öüM}0pĮ‹žŒO>DėV-1Œ­×“5M“žŻ÷Ēē«\§ÖmĄ˜Ó>Ć8,=Dʕ/ńś–Æ{CÖóY>žaķ¼ÓÄ2mls{s—2oŹ\¼?õ£Ū‹Ó°“eÉ“Łlņź?qgZX¦oėi”½$‰¤‡ć4d īęs0qāÉŁLøń>^Ī’yŠc$ņémņMĆłÕC8ęÆ'ńĒļīćÆ÷Ćr׊E-"²‡8ļŒźāļcOŒØ._tž™Ü÷Šć ’ˆˆˆˆHh h‘zśąćĻųąćĻČĢČųYń7‘•Ļg'Ų°1Ÿ–,ÆßŽŒtś éIڤ›ųĆļNēųŖŸSžĀżßēpŠ ż‰Ō¢kķ –½čÓmū…Wć»/™˜6˜Aū2¤Ū4&ĻtiČR¹öŚ,1{Ń»ėęéž]’Ų!„K6ķ’ĻĮ^µˆļ“½8øo` Nó>ą•Goį’›'ņSßć9ŗ„¦ł%“mӊ•«ÖTö-*.į±'ƲjõZŚ“n©‰ˆˆˆˆ4 Ą"""""õ0į³/łąćĻŲ°1eĖWnõóƂÅWNaY^QĮ²å«((,Ŗ×¾ÜČļ³Ææųž­&ÅōV3mź ܾƒ88øóŃ“VįĒügr ¹š|~Ū-“Œō9épś[›K¼fÅt¾˜Ż~gĮ°Ÿ&ńEqĆ& ²ņ?äµÉ9 ¾šĻæ_”“Œ. <÷N.y…7ęųˆ'Lҳ3šõk–~Ī[Ÿxt9ćJĪīNŲ4±›÷ _ē NśA ~³؁,ŚtÉ%+¾Žõ%J‡DD~ W]7Šū~b«©ž‹ŠKøļ”ǹźŗQ ˆˆˆˆH鎇ˆˆˆˆH=,Z¼t‡Ū]×eįā„ÕEą†°ś ć°ņOųų»m‹±.%Ó§234Œa=j1šÕ+ęĒgGrĻÜ8ö†Gyī¾œn|Įäµ¾-•1Ź|¢=;°fŚ·ä{ źVzx ŃHD‘zSXDDDDDDDDDDDdkѼ9’~ä!"į0wŒ¼…®ūķĒŖÕ«łŪčŪń›żśqŅ 'pņ‰'2 ?|>Ÿ#Ž­ˆˆˆˆˆˆˆˆˆˆˆˆģ9ͳ³¹ó¶Ń“nÕ€æE׿ŗ°jõjnøe$›6mR™aC†0ā¼sÉŹŹŚźõü‚ž~n Ÿ}1IA’FC`‘=$£Y3īukuńØ,ž®Qń·‘:į7ĒqŃē×ø.sfF½ś*232łļŪo+XŅ(Ø,""""ŅČt’'­Ģū‰Ķ]† øÖ!r*­6ŽGł‚õx€k „ųŠ“żöV“r·¶/. üģ=3—²ŽæĒײ-–•ĆŠ›u!÷»P\ę§ ’-“țIqóC°"~|e³ .|Š'õ~#›²§a·ģ€ķ+ĮW8߂ Ä*RÉuA’[h¾éJ²Ę ›ųKgXų)›šŒÕŗ V N ’ģ?'O„9®Ł†ņΧąĖiƒmęįßš.Ę¢¹$œšWĀŁł1Œ¢åź·Ł”{<¾ō$™so„dSp§ē."""""šŹø±ųż~üž­s…ÓĻ:‡X¬‚D"¹[Į5l’(O©‹ŽŚqįy®±ų»„?Ÿ}&3gĶdńāŸ“½”gŲ¶i6žvUiģ_Ś7,¤d’I3—PęšT¤õ h%)ĖŚŸ ±Ž Ļ <½;鉯H”św{ūŸ PŌłj23'bĻyŽ §%…ķĻ&Z½E’Ō /³„“ļ#Ė øć…ŠżhBÓߣÜI£hææ’Łģcģ¹ć(w;RŲålšõIüź3*\ĻhŸ‘öć½ÄcŁv¹ˆp’Žd®|•䜗‰’ßćlr;/†ÖQŠŗ]IŽ5÷ėÄĢżŁtąy“ģr‰y5œCøǐKł~ČX4Žų¢RŹŹżµ:w€h4ŗŻm%%%»}’ķN”˜Y˜†©£.=ń7X–µÓv–eqĀo~Ć-#G)h{1ĒqˆD£dl3xc£°ˆˆˆˆH#Ź’†cioBa˜DFG²W}ĄŗÖ}H O ¢4D"« ‘¼—)÷ŒŻŽžgɏoF«<"³ß„øÄÉ"B‹æ ¤eW6§CI‚ė&PRģĆ „šO“)l՛“š[8‡b“ZOä›÷(. `1šļČń“iž1¬T¾bõūƒKæ¢"'‹Ģ³‰Uų°˜ +Ž¢¢c`-‰ąŲ9ߘ1›āø‹yŲK~ ØūAŲ &Ōpµ;†“%OP²&³–ē."""""²cU£ƒ·õƂŒ¼żļ īæÜH§ĢnA EW €ėdPŸżjßöƒ¶F°ģ]<×%??ŸX(D j“Ē©?å"""""œ•ü/æ nfŁx%Xė§įvĆĖšŃ2˰6¦¦kŽŻķ·µ#`,Ą+©żó„¦[ŒēųĮ„Xø=o^©oó9'¾Ć-n†ÉØ9&‰bü`n>"3QŠk¤śˆ[b[ūSŲļvœĮ·ā ¾®XfĶOm×īw‘mE£ŃZ4o¾KśaĢPŗŠæõ™ØuیŒŒN-M›ašųāńx£>NŻ„iōJ±6,%Ö¦+ęŖęųż_į”ʰņV“Čī‚›×€o NQ`µßšg˜`X`xu8'oĖģ ¶Ź‘šĄšÜzõiŗqpēÓģėg(Žūj“ĮÕėźwī""""""[;ż¬sj|½¬¬l—ōļ÷ŹpĖ‹š¢¹*×Q~qŒVYįZµ-((Ąó”īĶ\Ļ#‹ŁĮ”ī Ą"""""žGxÓ·äwķMZv˜ĢĀ·Øš “i>łzČīJfž ”»ĘjæĶ„ņ5”Ń£™›¬:ŸÆ|-FoHOB~ja×Ü#² cU!¬ūĶ²e”xĒāeū`Ķī;††ž»ˆˆˆˆˆģž5€C^‘Ä:Š×9˜¦ ŖQÖŚ“Ł9łˆŽµk;}›6mRŠöRt"‘ĮF<ż3Ø,""""Ņ$X‰™8E·āt\‹oń b°cßR»ˆ@» ģEĖØŲ¢H¹»Ūļ-YŽė$,ŎOĘ]w,%ŻN#0’**2Øh{0!£vIÆ/ö9„ėŽ¢¬Ū)ųæŸ@¬¢eN"+ł6ńü@żā•œ«Mq—s Å^%–_A"ŠæonqügēPßch蹋ˆˆˆˆČ¾£j”oĖÜFßņ72š5«Žöæ’¾Į’„Ėøyä( ‹ŠvĖž³ed:+I}ušæ· 9ń°X֎śu‡wŽ{ģ]4m·4>ža`Ūvź!ŠFN`‘&”sĆ:¼nK” õ5ŽšVAž…Łv6^o¶wCĶĮ­zڵ„ō…S²ßiųzÅö֐¾a5E^mG¾š!Š»œ‚Æ×Pl#fyļb};ƒ˜[ߤŖœōÅ÷P?Dכ0ƒ&VlåĻS^Yžł9Ōēzī"""""²ÆØ廨¤„ėnśwß>š¬¬,–ÆXA§ŽøcŌHn9Š¢āāŻr &~ÆLF¬Y¶€'Ÿ}Ž‹/8‡ėū>;vĖW®ĀP]~yʱææXżEDDDdŸ6śūdµČU v”DšDb‡dŅrŚÓ”Ä|:wŁķ6mXG÷CŽS DDd«\Æ]ĒŽöųŚ“nĶŻ·ę¬ó/$#£5’Ž:ģö‘ĄR?CfÄł&»²h_%??Ÿ'Ÿ}Ž/&© Én±béŅ:ĻÕ`Łåā‘¶„cóHĘm»ˆˆˆˆˆH V­^Ķł—\@AA!7Ķ]·¦SĒÜ|Ću\wÓߤFdņ”)L’j½zō¤}»v¬\µŠŁß}G"‘P€¤QŃ ‘F.yäCMī˜€K7JŽ8yŸū¼¶ŪfŻś |3{6„„„śŠE~!*‹ˆˆˆˆlGvv“>D2™Ä²,@`‘=­Ŗģ8ßĻūža‡UXDDš$Ć0ˆ„Ćthߞ[oŗ‘k®»€?œ}īVķn½éF:“oϲåĖ}ŗ·n\yŁ%thß~«×+b1^}ż ^{ć?øš¹CdSXDDDDd;ü>Ēq0MÓ4«vŁsŖ Ą®ė’L:ų|>EDDš¤x<ĪM#GqėM7ņégŸÓ¦ukĪ=ėLśöé Ą¬og3vüx>żģsn»ó®­¦ŠnlźŻ‹Qū¾–L œ}ƟhŻŖžóQ}ų"{˜ Ą""""";`–eaFuńWE`‘=Ēó<<ĻĆ0 ĒQ@DD¤I+((äšėn MėÖj©ą‰ˆˆˆˆˆģ£ĘŽOƞ=ŲæcŸ~²śõāābƌæĖ÷ņŠˆ$ÖQ¼ĪĮ4MšÖßŌYQ†÷Ŗ]^»ų§ŸXüÓ}čŅä@ŅqˆD"ńč_PXDDDD¤ŽÖ¬YƄņ&]6 šdeśČH7±,(.ńŲø)Aq©‹[5ÅWy‚uėV™« ŠˆˆˆˆˆģC22šqėM7ņégŸsłU×pįyēŅærĶßofĻfĢøńōļŪ—kÆĪmwŽEĮ.œ69ŪYF¦³’¤hp_S?^Bžo&3#}§m_’ĻÉnŽ\¾4yža`Ūvź!ŠFN`‘z2Ük˳9éWæeüųń”••Wo LŅ"&7%9ꨣˆ\&Nü“z{8ą*€""""""ūæßĻ£GŃ”}{Žyļ}ī¾’Ÿµ»öźįģßµ+wŽÅU½žx<¾ĖŽĮÄĮļ•5øŸdi÷=ų£’v3>ß’gļ¾Ū(>ŽO²åķx$±cg'ĪpŁ‹$Œ2 “@€(›2Ź(P6…2Ź.Pą-³ -›°JĀĢ Ł{/;Ė#¶ć=$Y÷¼x*±;±›ü>-±žÓIwzī,=¾Ÿžē ¬w½ļœÅÜł  Ņ Ņ‚4ń˜ˆˆˆˆČašŲ?€ŲŲXęĶ›ļžÜ÷Ēxž÷fo"##™={Žßż£z@pK•("""""rŒ0ĘP\RĀŽ;yų±Ē«—æ’Ī4Ž{gZułįĒgĒĪ—”`ŒiµÆgÕź5Üuß_Hݱć€ūŹŹŹ˜öīæłĒK’§/rذˆˆˆˆČa˜`óÅgXnY0qL8=ŗ»čŅÉÅōéÓX§wGCa®z‹ˆˆˆˆˆ+¼^/÷>šW,ĖņėÕį·^^^>ŗć.Œ1x½ŽVżš6mŁĀM·ŽNŸ>}č“Ō›§“̽Y¬\µŠā’t‘£D°ˆˆˆˆČaŽ/”CY±¶ęZĖ‚k¦¶gPßæ'«ļŲI™»&ģ uņ»Éį¬ųÆźPDDDDDäXRW [XXČžż|›rŲēęfĆĘM›Ųøi“°H+”XDDDDä0åXL’gŽ’*”]nŚE8;,œ £Ā1¾Š?ß'dīū}˜9'Ÿ=™^¢Ś99ļō(Ā2³)-׌,""""""Ēŗß]z¹*ADš”`‘Ć“v·‹3KJ¹źģ(Œóļ,åÕŠIččĄé€œ|ƶ>žyO}ŗU¾¾ŸccÕ”ˆˆˆˆˆˆˆˆ4-u99LóR#ČŻ¼;·;·“WĪWbųye9³—Sēć®óčiWÆēٱÆ×†©EDDDDDDD¤É©°ˆˆˆˆČa*ō8ykV7ęT1ēo÷xf øĖĮ烐 °(ĒĪŖxŒmūųiU K÷tRŠˆˆˆˆˆˆˆH“S,""""r¾ÜM|Ä^Īį#ŠUÓ«×`ØéŁ.÷°2„§ēÄc4ü³ˆˆˆˆˆˆˆˆ4Ą"""""GĄx}qG6epÅØbƒq„`9Õ+ųŹŻ—šÉ*¬JĄk[Տ7n¼*QDDDDDDDDšŒ`‘#d€9)‘ĢŪĮ ųĘęĆé„Üb›s‚Yŗ'ŽbS•%""""""""ĶJ°ˆˆˆˆHńŁ+ÓĀX™¦Ź‘£Ā”*łeP,"""""""""""""ņ ”XDDDD¤o9.Œ1Õ’‰ˆˆHĖŖż9ģr¹šx½Ŗ‘ƒŠĄ"""""õČÉÉ£_r›7n£¼\›EDDކŚįoRߎäää«RDDDDDB°ˆˆˆˆH=v§eŅ9!Ž‘cGą TÓYDDähņxĖÉÉÉcwZ¦*CDDDDä tKDDDDŽygĘŲX–’ )ĘvķÉ`מ U’ˆˆH+fŒĮįŌe.ŠĄ"""""‡Q^^®Ši£ŹĖ½…„«"DDDDDP,""""B»ö‰øKKT"""m”»¤„vķT"""""(!:® g„%ÅŖ ‘6¦¬¤G@Ń»Ŗ2DDDDDP,""""‚e™ŁH’ IDAT9čŚw8Ę8(ŹĻĆėõ`Œ­Ši„Œ±ńz=åēa›ŠĻq˲T1"""""@€Ŗ@DDDD\Įō8ŽÜ½;ÉĻN£ø0Ū§yEDDZ#‡3€ąpbzݱ«Ā_‘ZŽJlŒQĶ‹ˆˆˆH«ݱė !© Š""""""""r45K¬€WDDDDŽU‡j + ‘ęŌdš‘…¾ ŒEDDD¤­j\ [»Ż¬0XDDDDDDDDšŚĄ }M£‹ˆˆˆˆ“-µ¶õę¹Ö!ŪÓ ƒEDDDDDDD¤)V|čą×Ōy³Ž"¹WDDDD¤õ°źmĪpežøZmlĮ""""""ŅÖy½^UĀ1 00P•ŠJ5*>xškü~ģw“†'Į EDDD¤µ³źn·Zū·hkXµŪ»Ö~÷×ŃęV,""""""m•‚A‘£«ĮpżįÆ©ėu'ĮuŽ[ēӉˆˆˆˆ“Z–iĄāʐŲ/¶ü׫¹Qgū[!°ˆˆˆˆˆˆˆˆ4VƒąŗĆßŗ‚ßżSąZKM„ž¼×(‘VʲhÄZ~mŪZ%«v(\W­wØ X!°ˆˆˆˆˆˆˆˆ4Ö!ązĆßzƒßżC_æ5źwM=ŪÕĮ‘ÖŲźnØæąÖT®X‘ėV‡ĀVåZ¦¦\w¬XDDDDDDDDßA`S_XköėĆ[ōÖśV?Gå²®—Õ;i°ˆˆˆˆH«āז=H&kU·‡­źuƒż‚ąZCC×ŃX!°ˆˆˆˆˆˆˆˆ4T½p½sžšZńnĮļž”Æ1<°j­źÅ57•‹ˆˆˆHŪ`«v”ę&VEøöݘ:ĀąŚA0•CC[uu®nŸ+‘C©3>Ų°Ļõ…æSüšŚ½~M­`×ļ¶Ł’ŁėŚ!iö _M큟MĶ:Ėkę¶ŖūW®BķQ -ŒUŁcŲŌō®é ¬į EDDDDDDD¤q¶Z=įļ½~k-Ū? 6u ]ė@ˆˆˆˆH+WēÜæT¹UA­©š’·²ėW„·~«Ōź lY EDDDDDDDD怸ĪyžÖüÖź\;š5 mŲ”Qɰˆˆˆˆ“ÖžA¬å×\®XĆŒU™żZ5põOüBŻźxŲc«°ˆˆˆˆˆˆˆˆĢ!z×žÖ ł\ęƒ©€k‚ßŖ¾UAqĶSÕō ®™&XÆˆˆˆˆ“N“T­Ŗ—»ōRčV ų\+-¦ęqU=«ē 6ź ,""""""""GĘ/6 `÷ėł[;ü5Ęöļõk >_9ž²Rʽl_9¦* ®Ųv®cH=Ā""""ŅJŌŻėÖņ»YWō¶, ‡3€€@®ąœĪ€š9€«ŪĄŽŹŠ·ā9ü{×Ó4W/`©ĒAzļ×ū·:¾õö¹:ü5vÅZ¶MYII=»ņč_ī ±SN§SØDDDDä˜SńÅH{Ņ3¹ļį§Ųš²‹ąŠp,‡ƒŠžæ 6U!pĶHŃVuė[½€EDDDDDDD¤į½Š©=śsżįoeĻߒĀ<žöĄüź„qų|>|>^ÆW5-""""Ē$‡ĆAB|GŽyõ9¾õ<śwB#¢*{ Ūu‡ĄX5£I+ų‘FØ€ż‡[®Żū·v¹j>³Źö K‹ łŪwrņıøŻnÕ®ˆˆˆˆólŪʶm|>§ž8€{ž°ˆŖ5°Øč\1'p孚˜šø& Ö0Š""""""""RĒ!ר= teļߊKRU’«(łŹ½ōKźĪIĘąńxT³"""""µ›ÕĘąńx8å„ćIźŁ_¹×Æ=]u«v»»j‘†Ŗ7®=÷oķŽĄ¦rhSq£rhƒĒ]ʃwßŖįžEDDDDĀėõņ×{nĒć.õkO›ź’jµĒMM›\9°ˆˆˆˆˆˆˆˆ4„źž¹źfĶ2S=’/•C@›źa  å^ńŪ«FEDDDD”S\{ʽæötÕ+PóeĖź¹_ū¼ÖD-F±°ˆˆˆˆˆˆˆˆų Ø’.SėßZK’ŠĻU=lŸÆŽ9Ȍ±IMM!33ć˜Śå "..žīŻ»cYu"""­ ąr¹*?Ÿ{ŌūłüKoĆ“ö:hČžeddß꟎įp`ū|5_°“ āŸŠøv»ŗÖ¬æÕ·DDDDDDDDDźSgljß2ū  Uć@W’gŒĮ¶ķz7’ššBaa!#GŽ!$$䨼Šļæ’†ćŸxT¶ķńøŁŗu ©©©ōčŃSgˆˆH+h#”––°nݚƒ~>·–6Ģ)§œ~LÖACöÆ-³mc ĘægÆeQÓ·° Tž£XDDDDDDDDéĄøŽįŸ©žŽZ½Łoxŗ:dff0dČ0ŹŹJ)))>ę*Łå ¢WÆ$VÆ^©XDD¤•“}śōcåŹåõ~>·–6LNNö1Y Łæ¶­²mUĄ–ĮĀŖ …«ĀŽZ½ƒ«Ņ_„Ą""""""""rūĄę %*~¦z®²ŖŪ¦z~ąŗy<|>ßA{ ’Ņįńøuʉˆˆ“’6BÕ66¬ń/½ ÓŚė !ūזU·”©ņŁT“·-ĖŖųI]Ć@׿DDDDDDDDDźŲģwkæįŸńæ`UżßAų|>Õ¶ˆˆˆ“Ŗ6BC¶ŻŚ0ęķ¬_rü¢ŪūM©UA°9phæÖ¹‚_©Ÿ£ž»ŒąŚ0ÕćAWmT—""""" fؙV„jJSÓą>°-®·ˆˆˆˆˆˆˆˆ4@Ąž=Jø¬T5’/µ.I™żÖ5²psö\‘¶«µ·ZĆžķ}P;®Ł*¶ŗCouæ^¦ņFÅ|ĄUó×ßŲT !-""""""""B½C@W©=tķ! MM諥"""""VÕē×Ŗ °LõŠžókčg‘_аŠPN˜0žaC‡Ņ£[7¢¢£ČÉÉ!%5•%Ė–3ožŹÜī&ŁŽšQ'ų•G ģīWžæl·_y|r˜_yńźÕ~åöįżŹß.šēWžÕ蓿ŹiE~å84äWžyĪ,ær·1æń|Ęlæ²§ĖHæ²Ė]ąWŠéäWvē¦ū•3˜~åŽķƒżŹŸĪōßœ‘~ÅSNć’÷~^†_ł‡%žõŪ¹Æ}õķčW޼Ļć’zcüĒĘ5KüŹ;öź—JPwl=Ąœ©5;pC†¤SĻi‹mõV;®k¶ŗĒÆUپ¶ņVåĆŹEDDDDDŚ&§ÓÉłēžĆ¹æ9›°°°īOLH 1!ńćĘqå„—ņį'ŸņÅW_aėļr‘uÅļ/į­w’ݦ_ƒ|ČŌ·²·ougąZs•éżGDDDD¤įLUč[1°Eå`;VeÆąƒ ė¬ XDDDDD¤ĶyāчIī×€U«×šćœ9¬]æžÜÜ<bc¢8` §œ|"ś÷ēš+/gԈįÜūą_Uy"-hʹē“éų C@׌?ą5Ԛų€µė|.}C„EķŽ“Ę›ÓŽĘgŪ\uŁļ錵«*„–]9ģŪ›Źqƒ‡’ņ^œÉeżģy,ߞOēÉqb‚£Ł6eģ`łŹ ¤uāW“#F¢Eäp޶ŃFŲ›•ŇŸ|Ź’„ĖÉĶĖ#:*БÇrĮ”óčŲ”ĆQßæÖPGæŌ:ų…žż5?ŖŅߌ]|kułUŽ+"""""Ņö%÷ėGnn.Ͼšyłyœ5y2œwķcc0@vv6«VÆį„—_%>®#7’ńŽó Ē1’KkŒƒŸ—­ .Øģ—łĖ7ņń3Ļ3³“SG’®Y`ϲ·ųóCs±{_ΰI €E¤y­XµšĒž|š’ŅŅźeŁ99Ģųö{ęü4Ÿ{ļü3C‡§Š’¶Ö0ĮX•½© yH{ė‹ ‹ˆˆˆˆˆ“%gžsN9K¦žī€ėõ霘ČäÓOcŚ»’ę’+®j’ķ.[<Ē;ķżŹrż3žÓ]žkśJüē¤ģqŠķEF“ó+ēś?_PĻÆé?§nD‡~å!įžĻ\īWŽ^ä’|żāżēŌ Œ õ+‡÷ö+Æ’y„_yLßĪÆPŸ’Ć ×īō+ŸŌk’śńļ‹9¬w‚_Ł.÷? üė#-=ĒÆÜ»{Oæņ޽«ōĖÕLŚrpd7uÜŖgĶ# ‰óó ø÷Į‡ČČ̤sbb‹ōD‰ˆˆ   ’Ķ˜É™“'µśø  €7¦½Ķڵė8aāœN'?ĪžĆ_~ŶmŪøźņˉŠj׀C›ĖēwžŽē‚īdĘ£æ¢ö+/›õW&=VÄ-ļ=Ķ9ķŪŽEĒ¢Ņr2ŅS˜pŹÄĘ?ŲŽĶ§÷ßÉ˳ČwŪXŽ@‚#bIģ™Ģč_Ķ…§"6iLk¢Ÿé{³²ŖĆß ĒĻUW\FnŻŲ±so¼õsēĶē±§žįÅēž!®cĒßæ*ē_|)„•uHHżēƒ.oŽ}8ZĒØŹYēžß õ¾śō#ū)‰ˆˆˆˆˆČ/ׄ_ĄŠ•+łxśē¤¤¦Š£{w¦œó†Ā•—]Ź'Ÿ}®Ź9ŹŚj\Te¼ķ? tć.RɅĖüüīčavģÜI÷nŻxéłg1¶ÆŁ+ē·g’š’͘ɾ}ūųņėœ5y”””­ņ@®[æž7¦½M~^>įį\ńūßW÷‚zÜ`ޘö6ėÖoąGń»ļX“)eį6 ‰=ćR«‡¼½Łä•Ł8‚B wJ 2Ų²<-+fńå7—ńŌć—0(L½qDDš²šŃ§ŸU‡æ=üWŠ‹‹(,, c‡XžöŠ_¹’Æ1ē§y|ōÉgüńŗkZ|’Ŗ”Öź\ZZZżŲś–·D;Ŗ„ė ­¼¦śŒ;ńdȏ‹ŲŽ£9ēšė¹xX,“Åģ—žbŚĻŪŁ“]@”$7™ &øXłĶ,–nĖĀօ1Snę®ß "²źĀfy& žż o}·’ķŁ6Ń}Žē¢’Č9}Ćūڧ1¶lŪLŸn] Šu/ŚIæĖžÉkuŅ VĶ|ƒ'ž9‹]kŽå±w‡óĪuÉVÖĶņžÅ_,`Ć^”pā”+¹ö׿‰$Æī™Źć ½t:’yŽ’ć@»?ąŚĖ_cūŲūųü‘“ /śž»§<Ī\OS_|‹›śķdś“’äĖ »ÉĢͧČmߟ‰^Ēg&qŠÜł`ūb”²ō_÷ńÜ7ŪÉČ-”ÜՎ®NąāėÆfRąŠś+ŚÄō—_潟6²×Aē6å@Ķ@%>2¾Ķs’šÉ²Ō<ʃŪ×ķDn|ōĘG銵ˆ¾eĖWpé%‘——ė÷9˜——Ėe—\ĜŸę±tłrUÖQ¶xŽÜ­—™™Ń*÷æg¬^³–›n»Ÿż;–eqóķw°eėVztļŽ“Ūæ-ķ7tEūŪŅŠĻ""""""æXwŻ~«_yŠ€d H®ˆEäčūļņŸ÷?hsū}äƒÕ60‰=œ^Ü’Š#„æ>ŸŻbŌŚCয়}ž­Ū¶U—’tÓ <`½ČČnłć \}ż1¶ĶĻ ‘žžĮ÷ŻsˆWŽŪćĮYk‘·¼vż»Y÷ęŻÜł©Å©×ÜĖ Ż Ū¾y‹Wļ½÷ó/ru?˜B¶ÆZ;Žą‘»’,ŲČ'’÷:Ͼ–ÄŁW]Ć_Æ aßĀwxö'xkĄŪÜ28(cÕėws’q\|ćĆÜŽ”%’~Ü’ ¦ŻĪŲ°Ć«Æü"Y™;8yČIMö%GHuD~AŅFČĖĻ &:ŖĪõc¢£ČĶĖkņž„y¾°°0Š‹‹Æ~l}Ė›b>žž9ļüēæ}üŌ ĪgźSZ¤22ŅŪōłųŅóĻrŻ7±m{ 7Żv;–e±uŪv:uāÅēžŽLæ(Ļ9µ• ×źß™Ŗc—ī~åć‡$ū•Śł_§NĀæ÷äŹķ©~åĄ‰~åĮŻüēŠ ˆō¾Xē@ær¾Ē.eĒŁ=;÷ś•£œžSB|{æ²»Ģ’ńĆG÷æÆ’µS}ŗ_yќ™žÆgČ0ærPˆǼ.ķ;ų•×ļõŸc8!Ö? ,w闧…“Õšź €k ?׀ė}Ķ1„_}įÆmŪ-^I­9Žŗm;'žž‰ĻēòźæzhY‡ŸmÓ„sgRwģ8äó{ę?ɧ=yąoX¦p’żl'½/y»ĻéŠ6ø3%)ąæžĢļ8ŠéŁ-B;däq}q2Ž{ę2’½nLnēčØ(^yéÅź S|<Ƽō±11ĶņĖ:Ō:ŌZGi±ˆˆˆˆˆČ/ÉČįĆøé†ė1Ęšā?_Ńčf"­D[įP±L£.p™CUsŽłŹ-‚rŠ1D5śŅ§1N¶nŪLŸn]1M~,Œ±+ ,, |[7°ŃcpDä¬‰ ŗœö+Ž{y ?mfŻN'öĶø®o±1uk¶lĮ¹ŗ+,œŅ¬Z»‡ŽkRšY” wuöķuv`@’Ž8–ļ$_¾ŹźÜ_ƒö„ļ>¾ń’t9eµ†Y(£“ĢąŪ³Ōrƒ#v('øü·ęģ6šq?bĒŽ„üżŅ‹łfā\0õ˜±5ßNcĶÜ\ł¹ŖoĆC÷żC`…æ"ņ»)ēńķ÷?°xÉRyāI¦ž?…Ή‰ģ޳‡÷?ś”%Ė–įp8˜tśiGw?/½‚ŅŅRBBBx’·ŗ¼É>…Š «Āߢ¢Ā­ƒÅóę4h½ōōÖ9Ttξ}Ütėķ¤īŲQ=ēoJj*7Žz/=’ģQļe.""""""""Ķ«­‡æŠŲų‚Äʆ>Ÿc ’xęéźųž‡å‘īkŃø-†æ-ÉŁ„½]ÓYµ2;¹KE/`ßV®É&Øwŗ8įpĘZvvīIĄOٲĒ&įŌy]‹ŪSˆ qש[’lšä¦°šóWxff6¶HŅi§’Ž^Éōs}ĒŅü%|5w/Ć~Mś·?°²Ü`…õ¦_—Š`;iĀń$~š[¾ż¬ī\1ń4ā7½ĖœŸæa® !ć&2&²1‘·ƒŠĪó…df•bˆĄyČ}±Ų·*"œI§sł9§[ͲW+`ĄŁ#‰^ß³Ŗ`!Ÿ—Ęˆ3Üty!¹Ž$μöaμl;oŻzÆoŲĪO‹÷pEßī8ń*~ŃT<žŠ+üłekH!44„Ēyˆ{žņ K—­`鲞ļ|¶mó·'žā‘ļ'Ŗ G iL¦*ä­ŗ]õŲś–7å>rĪŁgįv»ųŻłēQXXŠāu––Ö¦ĻĒŪļŗ§:ü}儸īʛIIMå¶;ļęķ7^k©_ õų9¼ųĻWøé†ėŖ‡€n ŸżżAļ’ūŪüņu/|…•ėµ?7­=Ä«[ō˜,iäśk_ųG£Ö_½Rē½4æĆźlh™!ū*ęū-÷ ’ņšßZ,Vų{hVÄń\|NWnüĻ#<r§÷0lūęMŽIķĪ…·Œ©œ’÷0ž·Żx.<óæÜśžĆüÕy g ģH`Ł^R 9ć“dĀy=Ō6¶nŪDæīŻ0Φčųīc曹”i><Žr*:2ŠqĢõüejRÅ/V‡_qéYŸ°źÓ]|’č%,xŽ…·Ø/Aō=÷BĘWVN@Ÿ8!ń#ŽŻéĆŁec»Ē7~A‹–PF#OE»Ę¼^g"Ż;`­/dÖcWódšŪÜ=ęPūā€>½‰u¬gļŠWłĆõ³é^DJ­é"NćŅÓ§s×’Ņ™ūōåœłF Až}xŖ>Śå›’Ćõ·|AI‡:†{ČHńFB§čĆźµ­ąWDŖßu}>’zõä?ӎ䯒¾Ēü?“³o±11Œ;†só}ā 6nŚĢ}>Ģ#ÜOLLō1WOEEEL½` Ąū|ø~{ĮŌ­÷Ł‡ļµŹ:ܼe }ūōį…gŸ!2"€W^z›n½Ķ[¶4ūö F3üŠˆˆˆˆˆC–._ĪeW’A!"Mźˆ’°ĘÄĄ¶}x}.mŪʶmž{ź)žtĒlŪ¾{xˆžćŁfÆœĻ¾ų’ĀĀBb¢£9sņ$BBBZżżĆ~s\Ó-ĶøÅ \ł8O½Ģ«ļż™¹Ó{ —>v=—ō:‚ē eŲuOńd»×ų׌—øwZ „u¤÷‰×pĀiɄ5ņٲr‹ČߗFŅń“Žšė AÄÄw$jw&łe;’.]#Y³+•Ķ™DÄõēŌ3®ą¦“Ūéb¶ˆq”““§ÓÉƽ†Ūo¹ ‡Ć‰mū(++£øø˜ēžz’[+›6ó—‡åįæÜGttT‹ķ@XXÅÅń‡‡W?¶¾åͱłłłGķķēl Ė.ĄƒĒć©īłĮ{ļLò,ŹŹŹše»•“bˆˆˆˆˆˆˆˆˆ1ėŌóÆ­ĢĆLå’ ƒ1`Œ]qįĖlcc슲mū0•ĮlÅO…yŁ,żé›:72wī,’’śю:\\ó-ģŽ³‡OŽūw£æeĖ&Ž?~b£óŚæŽ$22‚ßœuV“„æóēĻeāēšå@ŚeŌ IDAT>żÜslŲ°©AėöģŁƒūļ¾ėX:Ķ™»t-;7žĢeSÆĄētź7_D¤•hŠ6Bķ¶B` ‹[3-[·Ó³Gwž~ģѵźū|nģž%$$āpTÄx¶mHKŪsŠå mĆ4UµDtīܹAėķŽ½»Iö/##ƒųųų6yž˜p:Qķq8œX‡£ņ§˲*n[°¬Š²åØڲ°°*ćŠóŹŅpŃ""""""Xæh]ŗwWEˆH›¶+5•äѓõ˜€–Ś9cެßeŜĄ>ų÷Ū8ģ޽«Ł÷łW]Łfž·ŽŖß€zŲ¶ƒ­Ū63ØGwl…æ""­Ī‘¶üŪ nž{ź)īø÷~Œ1Mņ܍yŽ={v7jyK×QKl×®]:©EDDDDDDDDŽ¢6ų|处„éØI£däPœŸAļ†cT""­NS†›>Ÿ·»ŒW_z€Ģ̌VµmuZCˆˆˆˆˆˆˆˆˆHĆ“`l«¶åØHčĘMø^!"ŅJ5uĮē³ÉČHkµū×÷Aķ8‘¶£Mõ‘_žÖŽFP`µćDDDDDDDDDŚ’ €m[EDD¤ķµZĆžķ}P;NDDDDDDDD¤ķP`9ŖŌøõļƒŚq"""""""""m‡ę‘£Ŗµ·4°Śq""""""ŅvåęäØD䘣Ą"""rT©pėßµćDDDDDD¤­źŁ»·*ADŚ“Õ+V4ś1-»\.ŹĖĖq:ĒģĮq»Żø\A:KEDDZQĮēóįr¹Z}¦9ŲÖ^‡Ś?ń×"p\\<;v¤Š”CAAĒ^źv»Łŗu3ńńń:ćDDDZIĮķv“••I·n=[}¦¹ąÖ^ Ł?ń×"p·nŻÉĪĪfõź”””•š”Ō—łóē•m»\AÄÅÅÓ­[wq"""­¤FRRŗuėÖŖŪ0={öf̏É:hČž‰ˆˆˆˆˆˆˆˆˆ?ėŌóÆ­ģRb*’oĄŒcģŠ'Ę`cW”mŪ‡±mlŪ®üé£0/›„?}£i€N'"Ŗ=‡ĖįĄįpTžtbYVÅmĖ–UQ¶Xe,° ņ,ĖR…Šˆˆˆˆˆģgż¢ :T!"mŚź+H=¹QqØŚDDDDDDDDDDDDDD~T"""""""""""""Қ$÷ļGŸŽ½q8üū³ś|>ÖoÜȖ­ŪTIõhŅŲēó©FEDDDDDDDDDDDDäˆŌž8N’ūõS|M—––ŖFEDDDDDDDDDDDöÓ·O×^u;“oŌć.¹ņjUžSĪżĶŁ~åO?’ā€ūNgõzūß/Mę«FEDDDDDDDDDDDös8įoS|ׇܣ7Ą•qė‹)¤«ņÆ{Ÿ'‡Ļeī_’Ę«{ĀZ]Ż•u½†īĖš²÷yłĻ3™]īŠ %rMżõ—ŖQ‘żT…æG·GƃņĮWsĶŠ?óšņ`LåR«כqöeāļO`« Źtµ¼`B{eXÆbJ(ÉXNJ5d—;°¢ū‘Üæ3£‚ņīcߖå,Nuć±"ˆ8šŃ]Ü·–eĖv’īuąK8³†ä±sÖ2֕:¢¬ÆgoÕņż{ K& €ÆŗźZÕØ3ęĪĶäÉg©"D¤UŹĢĢ ..^”ŗS½·b/O›®/""""""-ĖŲ”ĻĄ)“½ī;ŗZłxņu\°›œŅ8:é¶øņĆ7 €ņ ‹˜_֍¤Įc˜ąĮ—k½KTīzÖ¦ŗź:€Įƒ†3$w6ó2²s6»īÅ;d$C»ī$=„#=śDćJ™ĻĘ҆Ÿwõ…¼öłPæ9""""""""""""Ņ*Ü{ǟIī߯ŗ¼~ĆF{ś™&yn‡oßĻÉ 'œĻU“ę³āsnĖæ°/ā8ĘOł-g Œ#1 ‹¬53ųšĆŸXVH·k§ńČØBŠ?¾…›fŲÅ]̟<…‰…šš«™tωœ°ž.}>…\HųÆń§ē³ć„Ky`E"‘c/åŅÓūŅ?.˜ąāt2v’ȗ/Ļ©3Œvw8ėϤģ«÷˜yāĶ\¤ó£„ÅĘŠĪ½–%۳ȶs)ŠźĀ™‰]H\æŪę3ær=OIGŗtˆ&4āa“+ŻĮŹÜLŹņ]  Ä ŚČŗmn|8U±ĶL°ˆˆˆˆˆˆˆˆˆˆˆH+‘ÜæŸß0Ń’~ó¦{rS¾’½Ć§nä¢Ó/ē‚E/šn†Ėo; ™ńŗƒ·“²h&_0ЉĒ_ŝ”™Üśāv¶|šßõæ€3Ī˜ŹŁ‹g²öĀIŒvmbŁ;Ÿ³“ō&dóEI×ņčUé¹w!³~Č"78žīĪ<²½‚ĘJäø‹Ī伒ąĮ”ó‰:7ŽwQ1īąnōģ™KQn bĮøĀ °ĮSuÜ,\11DŁlĖĄ–PŅ‘ø(„‘^ŠvEŃ3);%‹āAgpFœĮd.gъ,öŁ€ü`=}›ŗ°Óé¤KēÄC®·o_.……­śø)9F”,ąĆ'qĀ #9ė‚~üšbšßż„C/ä’īŔĻųģĘc-`cōĖüeąNˆÜHZŽ ŽłčxF^y“žŌ‡‰Š)’ńE^^„9ŌģNćédŁ”§-fᬵ¬Ļ±ńĪBģĄŒ¹Ž’×0’Éyl(ŸHgŗ£Ā™¶‚…ķ‡3¼Ļ8Ž/M'µ°įĆ65ĒĢ1€1}!e=›J*[ĖŅ“1Œ3{)KÄhĒ:{12:•Õ‹ Q#Fp\ÜēĢJ?x·īŗ†€Ž°iŪSR;j$111Mū|>† rй…wīŚÅ½>ŌśĻuꊈˆˆˆˆˆˆˆˆˆˆ+ ĪåÆóśź'øšÕ\3ō/L«uoT|”–ē’Ē›gT-ĶĒ*"0Š6,x™—Gż{%•žožł4‡sčČɵīGffMåģa7s×Pžó˜õÅg|²¢„’Zėy"OåŅó;ÓnĪ-¼¹=|ōXv.Ł+æē›•ADėAæ’<ö•WĄ¾°¾ Ū›.ł ˜»Ń LūV}ˌU`t”ļ‰A”nŽJVHB 3IßgSV<ÄŠĄFļOUų;įųqDFD_PŠ¤Æ·*L®+n+į/(iЕ呖¾—\O½śt"Ä:¶¶/"""""""ĶĒ2i¬žą÷=…‰ēOfLVyõ}ƒ…żÓ“<ų½·²‡.X¦¼}½5- ÉĖ+ĒPŃw×’ē7XŌu)Į•÷-ÜæŒyCĒ3vČF ?…Ó’Ų“OŽĶ³[«׋?ū"&Gyqžņ,ƜRµ“ Æ9Ÿ«_ĀŸÖ?Ćo’¾Y²ų\ńtO0x ŹńÅ&s\b …k÷°×XųB{qÜŲd’ĢfÖnqc·‹&ĘĻ¾’ŖĀĀŃm}«X°Ū¢,±”Ņ.éŌĪGDh ÅÅ^ąą=€÷ļŻäbĀøŠš`åŖÕMžšė ŪRų €EDD¤.ŽtżkKŚ3jŅi ŒvØNZŗq¶ŒÆHĮÄ'”O'Bź:&åylY²Ū3É+3E'sŹÆūQ4w&óӃpņdF'øšfūm ĪLy…ł%8ŚÅ®V®ˆˆˆˆˆˆČAe|Ä3ĘqÜoĖņ”2³+ڱƒl»={%żŸŁ,÷Ōī~kŽA×qÓńå”äbĒ_ȵg-fķ'„ø½^<<čīÜŹ¾r5וŒ€Ė—Mś’ĻłtÉW|™łO^ūmŻ:;aKĶVœ{ę3{v­>ĮVG:DēfÖż¼‹}ił:€-ÄķHBÆĪąŲųŠsČYµˆå;+NW—žōóaӋäq½HĘąLż–V¹+Ų½lņÖ¤’iąL[Ķ’NĆz¼¶˜…{}ŻŖvūéē_0nĢ"##«—0a¼ßżM„vÜÖĀ_P,Ņ<Ģ>–M’Œ%ūˆ:‰3‡w¤źmĢ»õ;¦Ķځڟ³¦O¢29 ·āݬZ²ŠM{²),³q…וĆGŠ'&S¼—]ŁexL»²¼ ŒR„Uroł–węģÄvtdō¹g1$ŖÖ¹o/‹¦ÅŹ\h7ä×\0²Mõ6ą1q’¾ä;f­ĖĒ@Pˆ DØÉfŻĪB<åE¤ģÉgdBÓķCk>gqoå›’Ī!ÕnĻčóĪfh”ŗ,‹ˆˆˆˆˆHėńļ7ߨóvm—\yõ!×iZо}“÷ĒÜŹeŹØźź¾łsŽŪ2˜[ūüž[īJä›E”Å%3Ŗčł ƒ¼š‰œÉz|ŹėOģĄyŪmüž“?pŁŅ'xy÷JVfžĪȄ‹¹žž~¬*éCßžŽźœ‹śßŹ+—ŗI[“JŖ»=Ēį*ßȶTŸßžeĶz·j•s$§&c`Š2ę’g&³Ėua½„ę­fŃu÷²õmśŠĻ6Õ’X‡7… ߦ°”2Ž“|Y¤-žIĶ¬Óæ~UēņÜÜÜ&ķŸ~žłü0kv›;n €Eš“ń°wÅ÷ĢŽü5§&E KŃ"Ņ*”g°čėļXēĆX–ea—¹3.Ć*ĻVT_ʏó°­4–~=žÖzc§øØcĄų²X½rżOģV=PMɶ•¬Ļ³1X”cÓtįėĒÄĪ`ūŽl‚é}ņłœR}œ<7²7¾ō śōżE„æ 9gĮĘŽ¼)‘6hż†$÷ļēWn.N÷r¾~ æŗ„]+—Yö–>’W’Ķ…œ?t,'QNIz*{ÖŃĪ2DN¹Œsc3Ł9ķc~Č ĄżŃ&N½a'_2ŽŁO.göæ>„ū„'2¾Sśī\Ą’ŽK`ÜŌŽ„ūv³aß`ŽźĻą 7ī¬E,śä]^O ׁ—:ķß«·ŖĆÄńĒcYł,X“ØY¶ßĆ_P,ŅģŒ)!ež,‹:“źś•ó²gé·ĢߒKa©ŪLT\Ž=’>Ń`ļcżO‹Ų™Ka‰ķ$¼C7śv %gū6vļ+…ą(:÷Ķ„! 5s4Ś%ģY³˜%v’UbŠŒ£ēąQŒīC ‹Č1ĶĪJa[¾ćˆfšä³“žöf–S¦łö²nńj6{#°»3Ŗtoæ Ļ~łš#v8SĪJŒ9VŽs %Å%•#"JSÖ°iXWGZ`ē°~ĶīŹ:2ųŠ‹)5a¦„“µKY²~'YÅ>#:ŅsąpFõļX3ˉ'‹u ±*5‹b_ķĀ 6µ¾¹ß1ŪŽƒŪ˜2¶žš.[+|æ>æ/雷±#ĒĀź1œ^‘ކ}&jū °tŁr|>£Glłs¶fe}ō/ĪNc™zęĀ­C|֚¶-^ŹŗŻ{ŁWXŠĒv“Ģ)g “¾P,""""""M vļŽCyģégšeV?yžŃĮµęIī©Ž½0œīķlųšqžp’g±`Ś•\: Ą —?ĀķW×ÜļHż„wž„wj=ję1X¼Ē›O½×č}·|Kųž®Kųż±~¬Ø=üsmŸ~žƇĮ²,ņņó™·ąg<*¬ż–ˆ4+‹ŠšpœåŁ,Ÿµ˜Żīŗz&9±>ÖĻü˜Ÿv»IŪŻ#«x+Ė×ēa;āuĪ vP“ń[>œ·›Ō)÷L®7ä‘cą)&™1É;ųn]i+æćĆ5”tčևƒŌ1¤Īo‡9FqŽł£鋾ākŹpD&qā„$BK62ėXyĻ1e”ŗĮą }ņ Xµ˜][Ö³cHeėR(µ"88‘‹ÖSą.„ 0Å[Y¶!įō>é,NīBįśļ˜¾`7{׬fĒ€“čQŗõ»ŻG;L:› ‰As ĻŽŁ€6Œ¾'ŸĒŲøŹ#gö±«ö.7ą3!“ąH¶_žVY±j@“õnŌ9kE3xŅžs7ā³vŅĘĘ9°m‡>+EDDDDDDDZÜžĆ>×eś_Ŗ¢B°HssDŠwā²>’Ž Ūš“;Ģ’~SĢĪ%s™·>"Æ©¹ŠīšR^^Ēó¹:ķ`Kx½^ŒF‡öaX»óp»ŻĆuęģ%Ėg0&“EæEķ‘ļ­¢B €EŽiĮt{6öÜΆM[ؚšNÖö•ĢNŻLŹų³8½od½Ćž–¤žĢksńvdŌ)ćčjįŪy ½ē7enVAQI$õXĆ®Ķ;X³Ų;­gĒž Hō’a^7n윽dū VpgśõĒ“ėŻ›ų…{Hõd³7Ϧ[é>rmƒ’@ĻNƒB;Ī&Łe»Ÿ Įy‡æżżĆß*Mž9{XŸµT|“TDDDDDDDD¤-R,Ҭą.Œ8€Œkٽ«bøĶŖ ՞K˜µznWĘõ'>0‡u ֒ī«ļÉT]“¶MÅ%lGÕEzSqQŪŖśKƁ]ˆ¬uUÜ Š'\ᯈą$,.‰qIŒ›ĖśŁ3ų)µ„«7‘•4’Žu<Ānfī¼­šzŽ;‰!±žļ=ĒÄ{Ž·Ē.‚‚\tNīCō–•¤oŚV=“{Ó.h{ÅŠĘCĆGŸ©¬$cć3M¼Ė :>‡·żśĀß*Mꜵ*_…9 ×r£?kEDDDDDDDDڰVcXøp!óęĶgŪ¶­FļŽIL˜0žQ£FaYJ°¤m ģ4Œ‰vóåšÜZ× „x 8Ś÷f؀ބŚ!ģY|d„ѱÄ8¶‘į+„<¬'CśĒą²ĄWVH™3B½EŽu¦=ŪóŠ'&,‡3ˆ°Š&ńzńÖł˜Ö’“ˆeŃg<’Ā«æČrL½ēn`9 Gl?’ć×2?­Ā{3°[0Vy@EĖxq{ ŽŲŽ“wneOŁn6¦‘Š+„Ā­[I· –+–Q!±Ä:¶’^¶“ [ HģŁd»Üćcboū#†cÄša­ćœ pįrZą-b_®¢‚0¶ «Y>kEDDDDDDDDZ«VļŻ»—æ’żY¶oßę·Üķ.#''‡E‹ҧO_n»ķVbccu„ $~Ų8’wĢ`m¾]¹Ģ"¬},”Ö^ŠÓ3ż³¢\öį|åVdC{Æē›Ķ…¤.˜ĪŪK‚qY^ܞśœ>•»8u8DŽavö&ęĶ^I®maYc ƲļŅ…`æ`¬tū–¤¹18ńīYÄēU $l…õįW“ś;ļ9å^¼¶pZ`…“tÜ@vYŁ„ōJ¦S@E󪢁åÅć5XQ½Öo-ėņŁ:ėCvĪwāóxń@‡ƒčęĖ•ÄŠ¤õdn* å§Ox{i¾ŅŠażp—ņ™`E4ßö[ģœ5ķIŒ dė®2¶Ķś€ō…”öęŒsGŪ Ÿµ"""""""""­U« €÷īŻĖ½÷ŽG~~ńńńœsĪ9 2„ØØ(rssY¾|9Ÿ}ö›7oāŽ{ļćńĒ#&&¦Ķc ‹/&22’žżūė¬ü% ģÄšŃ½Hłn ÅUæ„ #8m¬Ÿ×ī$+'"GŅ!¦ѮƯP0ŻĘŸÉķ–±lón² Źp;\„Å“'Üé‹ˌ«=={%šžC~‰Ÿ@HD =1rH|ė1”ā1>ʊņ)«¼Ēņ•R~ ½ēƧ²‡““ŖŃ÷ƒ:`rēZ+YΊpŲėÅćp‘8ś &‡,fÉĘ]dŪFē4`$£tØlŒŃõų3˜¾”å›w“UTL™ĆEhT ±‰1Ń^7äų4ēö[蜵Āč3žņę-eSz>„„īÄg7×g­ˆˆˆˆˆˆˆˆHėdzžµ•ƒŃšŹ’›Š¹ā cc*n`cW”mŪ‡±mlŪ®üé£0/›„?Ķ<ģ1Ęp×]w“’²aƆsė­"88ų€õJKKyöŁgY¹r%}ūöå‘G9äpŠĘ®¾ś>łä|>.—‹Ī;3iŅ$žüēŪ銔ĆQ=‡rÓM7róĶ7ė¬l#ęĪĶäÉg©"D¤UŹĢĢ ..^”ŗS½·bż‡'"Ŗ=‡ĖįĄįpTžtVōtv8pX°*{>[,‹Š2Ve÷ģŠv°¦G9ŠśE3‹-⤓NĀėõž?{÷U•6`ü¹wj* IH/@(!ōŽA:*‚e ŗŠå³®WWĮ†®`ļ  ‚Š ŅĮŠ{ BB*éÉ$™™{ļ÷ĒDI£­AߟÄĢÜrīiS2ļœsxńÅłā‹9dffұcG¦M{žĪ; iƾśļæ’>YYYDDDšžūļŃ£GZĻÕu‡~„Å‹“‘‘@ūöķxžłitļŽķxž&O~ŒÉ“`ęĢ¹ņŹ+„‡ !„B!„B!„g(''G*Ań·Ó`Fųą}.»l4ƽö:÷Żw/ńńͱZ­¤¤¤0vģŲSĪżm’źÕ«é޽[µūj:WÓ“Zó„( v»żųØfqįČĪĪ’JBČs”ŌzB!„B!„¢Įh0ąŽ½{3ž:ČĖ/æĀ½÷ŽSmø¢¢‚3f››K«V­čڵĖ_ļŽ{ļaŚ“i\|ńHZµjō7ŽČĖ/æ‚Éd¦Wƞ”•9((Čēšk®!((ˆ &0mŚó†A÷īŻ(,,"..–vķŚÕzn`6Ó¶m[¾śź+ŗvķ†aÓ£Gwé” ÜM_K%!„B!„B!„Bˆ£Į€EįžÅ£NfćĘ <ųąƒ\~łåtģŲ‘ĄĄ@ņóóٲe óęĶ#;;›ĄĄĘÜwß}g5uņ}÷ŻĻܹóxüńĒłā‹/xꙧ ā³Ļ>å¹ēžĆßߟĖ/ĆøqćP…©S§Šøqc>üšC¦NJpp0Ļ>ū ķŚµ«õÜś”}źŌ)Lš4‰ėÆæ???üßB!„B!„B!„hąUÅn³įēē‡Żj• gE7 œ.%%%”——ŸYŸśIåėd· IDAT†ē¦QõĻĆĄ0Ą0t Ļ tCĒŠ=÷u]ĆŠut]Æś­QRxŒ«~<ėBåää0}ś RRŌxL«V­øļ¾ū ’^ ž4+W.ēõż-„"„BqFμ æ€`TՄ¢ŖØŖZõŪ„¢(žŪŠ Šā¹ÆØ( žū( @Õ’ĪźK‘B!„BńWµū׋Œ”Šē]`` Ŗ¢ą(Æ ØHfgĒl6ććć·—¼ü|²Ž„M‘§—FC+T“&Mxī¹gYæ~=«V­&%%…’’üżżˆoAæ~}鎽»|Č%žŅy™Øpé“ µćeUŁy“œę!6¹ż‡ŪqA6|ķ*{2+ˆ“Źķ³ø½?»‚&ž¼Lrū,nłš ņ1“’[I€·éœßöµ©•k:4y¢B!„B!„¢šöö¦øØ˜’’R© qÖ4ĶMiI)f“éŒÓhp#€…øPœĻĄ]b}Ų—UNi„.-„8.ĄŪDÓ`[Ž8¤2„ų ĄB!„Bq~Éą“łÉ—£×ņśmo°°ųŌńī€Ėxą­‰“u%’\Ų°?›łßYLÜõWt“Aä'&*ŠK'ēT|³8ޤ§ŸŃ`UŖOˆ†EQ ³Č)Į_!Ä) [Ó$ų+„B!„B!Όī7”qĆ8u\”™ąW1Ō÷Āų\:ućzVķ/‘¢¢1 0©2ŠGQ½nq>R B!„B!„⌨EeX/CƓ—Ó|põPٹ¦ ¢»æx×ÖČZ»BŌÄ,U DĆŽČBZ¾S*B!„B!„B!Ä9cŻó%ŸÜ̵ƒē°b” Ļx_ßĮW1r’ē¼ļ÷0—Ÿp¼n£Ūõ“˜ŠÆ1–lR×|ĘÆg_¹ ˜šé2‘;ÆėK×o,‡I]õ.O}z€£ę}y––ōc{4£i-w7æ|ƒW–åQ^u]ĆI»kīąÖńÄŚqä×uj•ˆć¹ńvŗ•^Są– —rÓO {č ļŠwó*Jŗ¦{¬%c?¼ūķraŌ#=!žjd° PZĮ™Ū–ŠĘ–,•(Ä_TŅ”2©!„B!„BqFż KēīĘŌ(ŗZ=į_ŻŚĖ/v²jŽ6ņf§TŃrŅ4™Ē;÷ēźū>į§Ø‡xv|īą+¹ūĮvx/|œ{&Ldāӟ1{]EFķū·wīz½ń(·ßz?ĶUˆ»ż_LŒ«¬ŗ°Čń/ņ|ē,zęVn¼’MfkƒčéŖ±\†)–ŽķJŲśö}ÜrĖcLŁÕ‘«ųżķŚ„'ąNF Ń˜g8“‚NĻā7iÜa s˜›ŻƒJCę †įł…!uń×½©…h(4Sõz­ČŅ B!„B!.ś†Ļłlܓ\ßw–µßX®Ģś„’;`%ī„#ŻA£ø®ß6ݱ†…`#ßĢŽÄÕ÷÷…wæD Œ"֜ů{“Yj‚ŅķüZunmūż ›ü=GKg1oä F·0ĮaŠü†3nX.+žś„Å­@«>^Hļ‹:×R.+¾įēf Łśł"~֗.Ѳ,ėLŅāĀ&‘!! 3Ś@Å]–Md³.åŅŹē;>:Ś›ŌŠP©Ōæō{6GŽ‘Ŗ°†aH ųÆ*ĻźEYN¹T„"EQŖ‚¾ ŠŽA1B!„Bqįü]ėŽĆ÷óŽ0ńŠa“Y“Nüh?¶~°žL#āäpX M­ķi;m&CŖ¶f_BL©X/āĖŻĻšÆWŽ”ó†U,’i‹¶SQĒ>£Q=ś÷”g›0BüƒˆŒtį2{ÖvGĘÓZŻĮüƒ'„°NóćNµ¢‚ Af㜤'ąFĄB4@õŚ×TN¼=·¦±³¢eÕ«·‚ah˜ŠvÓ4,–»½6šćŃH–tĄYß’z ĆŠ1tåĻ —śBˆ’±-Ę0tT  !„B!„ø@ø×|Īģ±÷sżi^ń ģ85\¤ø+©Ō6³ōĮgų,ßrź~×>–=q=›Ūa䐞 Ÿ|ć·=Ć=Ļo"µ–}}pÓó7Ń3émޟż5Éyčśų›Œ=ž°ŠŖš1«ĘY•‘sšž Ń˜5LmQ4bģY$x§ÓĘē(^¾ž”Ūæ„S«ŽRŃŻn ݉;w'ÖFǚhė{”3Pąö“ žĖ½_3Š5·ŌƒBü tĶ홆_bæB!„B!. Šk+óēpĶ‘ģn9‡tÓ)ŪZŅö³Ū} =;Ūł|©†^mJī\Čģ ™5ļv^š>†įįėy÷صĘ}Ƅ āRėwL’t›Ü (>h'¤hŹJ%…Atl­3«é¬Ėz®Ó;†É&‰ż6ü"ś5³Į±¬Y¼„%I‡Čr* ųįŪ–Ž dPĒhBķī[ĮāV°ro!t[sb:uå²KzÓÉ;m?-`ĮŹ})W@ ¦åŠŃ ‰Éc’’ü|XÆu³Ջ!mżųéǟ<|›Ņ®Ļ.Ō–f^eänł‰…?Æ#éHåImsŠŗĪS|hÜŗƒ†b`›`üŻ9¤n\Ź¢%æ²>ķŌYŪÜó5ż+‘÷ėW|³µōxŸÓ}Ś2`LOŚźŪykvRŻżŪdĮlCsį®*Ą°§ß”ķ±LłsF¼0‹˜½s8°d.ĖŅN—\ń:£Śz1¦­×IĆźŌ¢­Œ¾å…zµłÓ?ńžOśXś'–X²›ÕėÓ):ßM° ЉS@ū›ĖhļsˆDŸ4¢l¹Ų|šŽŻÆ&«7ŠÉFīęŁĒĻÕQŃ Ļāõ  9r0Ł“ņ‹ą>óęåtcKYK©äæć·’tM*C!žŒēa]«ZŻų}M`!„B!„¢ĮÓ©Xö<g›(ަV,TĖ~fīākx÷†¹éŲ›ĢŽ^JeP"ż“Ł˜Rę߅!]ĖIŪvĆ„>LJäĢbM‰Zė>“)›#}10‰C*±vĖØ¦•°Ās]sĮ|½r,ÆŻvWĢųŠ%ŁAčL7S>æœAIĻuz§ĻŽ›Į\:øĮ‡æąõoxwĘÅ#® ‘÷<^’é®×Ń­]9ń+łśµ#äŚč5lƒ.½b!?ŅqĀö ­z‡éŽ źŲ‡ž…y|żkĪØ¶“‹VŠŅw°żˆ†QŪgjń‰-‰n’ćé ŽĶé>r#ćKH™’*_—¶ Ū°A\6ĘŪ¼YžV}čæīóģ-ś1bp[¢²ęńęœĆ÷`šČ n°žż穾-tƕ¼(Ü4—¹Uä“-QŠÕ;SN?½÷߬ÉĘńŪFćÜ|oŹŚEnb7ZĒ›h8U™ēž3% Ńż6“ ;Āęä&/üŚc¶Ų0 ­<WŁ1ģĮ-©(ĶāįØW±YĶT*čīJń¢Ø U¢§ ŲPF¦3ˆ,WTō_…a€”K=!ğņ¬ƒ¬».„B!„ā¤:’¼jžÖŹĮ‘ļįĪĀIÜ6é > 6”bß7/±1„Ć'˜Ų¾—2ž¦Pšx»(OßĢ//}ÄwÅfŒšš÷YK>ēÕĻę” 3xļęcdl]Ģ/Ūŗ2ąųu‹Ł÷Ž#L½évnx|“4v­=Bšīs†K÷žėōNī՗–­"ń/ŪĘŚ•IģĢ6”gŁkƒ['ĀO‡°ģ‡_ö'¤³(õjLHßH¢¢‚įP6»„ā}x‡’w±«Ä—Ų–ķhN³‚½M3BŒöļ:HŽ^{0QoŅ‚V”JN2`Ę?¾Üd­ū‘ļÖ„Ä8L–aĆgh<‰mbYžę š6żĒŗśļGŻó sÖf×}^z9M"Āpe°gėVödŗ0²V°28ßų°’yßóĶ{ģ9­†sćŖ¬Ąįpü>Gm?S¢éJ³ęį¬ŹĢ:ē啰 ŠoS@7µ„ㄺńó±{Öz­ČĒ岁É&ŠŁīyqvֆĄ²Žm¢4c'žcp§”č.ÅøQ]%ųŁģ“±š_‰aT>Bń? ʐ°B!„Bˆ Bńœ¹ØÖ?rsY÷ųŬ;a“¢ērpīÓ<4÷ŌĆĶ™‹łxŹb>ęōöa‘śĶdīüęčóųā„@“Z‘Ģš7ļgĶ›žūīąxvh8eĻ1ėyR>—žkKOy)īe¼ż2Ą«^éOŗ|¼@­tਚõXu¤r$-GhÄńON¦Q^^Ž£Üy|Æ· ÅØÄétƒ³’J·Žf¶ąӖv‘ zśN¶§×õ…čHBLnņŅ‚Ś”°°ģĪRŅ2(5<×.NOēpa4ķƒ›ž°wP(ņP}lõ;ĻŲMe„ s£`B›ć»/“bÕ_ĶYxVu:xŹ "³ÓQL·jNøšCŹź…Ģ_²‰¼‹ßfD 3#Z˜Q‡aō®EŒŸ:‡žĶ”]ę\ŽūpĪ9i×ėŽ|ß ß±cѬĶ1£ućāĖŚ±ų£ėÓ+pMēˆ3’ÄØh@ĄBü-ü6t¢×Bü-€‚nh(&;˜ģžĄoUŲ@]£¢ą•ŗ7”ń=ÉŽū3yŗNx»Q˜¬&t­EUQ‹«˜6ö½,+ķ!-„B!„B!„¢į‹kFĖü­,(67Ģōj”:JØt…co҈€F*čøŻn «õ')¾„„4ĘK)ćȱ Š­D™}ńńńBqūącRP¾D“nJ‘Ė”ü@Ś’7#Ū™)Łü#ó—$±-÷ä™# S3‚ƒü0™äå•`˜"ńó5cčE•ø0ŖĀ†Ji1Åe 5ņ=~ī®·'±ė·t,ėq^ Łū¶³µå\ył-„Ä$±½"Š–•d®Ūtv•jŠ¢I“cč[šń798b/ā²Įżҧ”éŸÜĘÜ®cPN˜śĢNÅd±b³ŪŠ«āź†ī¦Ņun¦V+s9V b„…œ—~'`! “' --ūš²†`` ˜lžQæ/T«?ŠÕÕ⋁†Ž+w'„eVģV3~Į±T–ŗīC¬14ŠģˆMŃPT7Š»?£µ˜bŻ_*ūO„īEø³œmyś=†÷½Ū#p–Õøž„¢š0Lf&½™Éb“¦B!„B!„BŌČ՟Ė#±agŁ}¹źśV8~ų/[ŻJƒHļtØ„k8tŲŅ„ž øØĀ•G(ėĮĄ]éŌä`µē(aķhßĀ˱­ģ>XØlśõŃĶBh‘Ų™ž%ń4µ;ÉQ w޵YWŚŪ¶šć7.B:w¦_Æ"¶-ųäǦHü|£”R‡‚aņĀlÕŠŠ“źBsćvƒ”˜ŖĶ_½Ļ+Hfė†MÄE`ųÅ×ѵü0[šĖ¦½ygŪC(9²‡CŪ¶³/SĮČŪĢĘŲ@ŗ…Éē¤Ż“&=5č".žt| E›ē1įł¹ē¦c%”–9A=?q љU…psf\Øf;ŠjA±ś£Ų”˜})+/'’P…‡ŝ·Õˆ5“|šõž=øf$#}’F2SÖa ī@c_;fōæÅI¼%™Ķ•]¤²Ļ#Åb„w—ĘŒjéM¼æ ‹®““W֝yĢÜUI¹ÉĪ•—DŠ|óa¶å]Ųkų–—ęqó+ū1\eč.†«Żķ—Żķ@w–ńż'Sp»Ź%,„B!„B!„Ø•Ł7–ı7qćƒ^˜ņ’ٱč_üė»RÜ $½Ób䓽y)‹, —ŽĒ=+)ڳ‚Õ¶°”Ōū”Ću{,{u¢õ([7ļā`„ @łĻƱ"Æ;£.Ē[ ~J"Ó/‘Özūņ BĆ\TŚÅ–$'Įqō ‰?¬z«Ū½±X@q»q¹­MWQLfĢę†(™­X- źÕ×PżĪó”Q»”\qQSü¶¾Ęä÷ŲŪć²!×pKP½¹ōœU±ā®¤²Ņ…ÓŪtĪŅ4e­eŃņļłbeę9_ŲƍĖķÕr~Cņ4"DĆŽČ‚Oł>Bü¬Øäēs¬ ü¬Ć”Ū‘ƒ 7Żlń h†win³nĶzRZ6ŸĘ(.3YIÓ섆ŁŲE¢y§€Ļ#ÅęÅ —GrU€›õ;óy#ӍÓb&<ŲN¬œ±ņźšE5ćH]…ī*;õÅŅęŖI:Ęi½1,dĖ7³ų5p ·ŽD•¹Y,{óuÖÄÜŹc—Å žń¾ŌB!„B!D½({?eŚżŸ6ŲōNūśĪ®šÅ««fUmń"¢ß?ˆ ­<é8C ¤iĻA j^IĘŖ•¬>čä·”CŠ;Ģi¼»Ń³x²)vW ķČ.ö&ŅĢŠ)w8(Õ+(-sį:5ø¬ø54°Ŗ˜TōLJĖZ¢„6Āßϊ’ķ™©ŅšņĘŪīF/-©¾@õ8O÷H«Öų•neżśŻ¤ę™`٧¼Wpć·N »]nUÅl6<פjņä×]SČž?榔(ĄŁŒ W1©&”ó”w Ń„8cŚ…·źfĒ–µģJÉƦVbV54ŻD–† ŖBc»7^XŠ1”& ÅrüÉĪŠ 4ĄĄL„©1„„ÅdģK'r`g‚õ#˜p£Õēi@1Ó±k7¶ó”©7ä–ńżŚ\¾Is{^|3]{6a\ ;±ž&¬šĘŗåG˜¶Ē”˜hŪ)„ŚūŠĀOÅpjd敳xe rŖyb«ķų\}ś‡16ĪJ˜Ÿ ;y¹„|»"‡YUS(×#Æ}ś‡1¶©p_:YéÅĢO1h›ąGĒ3–J'›¶äšś¦rŽæ¼©ŗuf\+oā|”(§ŒoVęš}vuS7+“鏕|žM:³³kݫк_Söó¼H­łįĻ&›j¬Ļˆv<ŃӛP»‚»ĀÉÖķ9¼¾¦’†Šq CwU_J“ E‘š—ē]Ģ^’?÷xŽ9÷vĘf88ŗc i¾éŃĢē÷· FIó¾ą‡ŽqĖąČ’M–ķå«’¾ÄG+ö’]a¦ÉČ’šõæ»ST]žžŹjj“:Ļ+`ēŹl4Žó¼šĒūāpR”y”Bk$±AV©!„B!„B\tßf4 ĀV±ć÷Š/”]‡sqw_Ź·|Ēā¹”Õ2eØMhŃ&†=›½»“ėŪ§¢āeµ`U4,…S?«U\%ø\į(>6lVPJ’““HEBŃŃų„¤SlØx…GåmP™–}üÜ6·¾EļF»Q·.äŻ%uŸ§ū“Ąn³a?©N É/«~ŻŪ£©7÷#4<œ0s!GŻž «58„P/=» ž•¬(ēõ³L·ę‹ÅbÅf=Ć«(6l6 eē%¢2« 6£”–ƒŽ×éq‘†”¹T;ŠŁŖÅlC1YQT ˜¬(Š CQQP0t†»?w±ī “Ŗéx5§ÅdEĶ]‡M©ĄaųÖõ,D«^‘ü§,_›ÅGy›č—EbūśŸe€‰M}+Ģć„_Ź)ULč…n ZöˆbJ…¤_s˜š„”ųłqÓ?Zł9§^«ÖćsMÄDz˜wŒ’.«Äi±Š½K07_Bś§ŁlrÖ/Æ1‘^ęćł%øķv.ķĢķ}*łq]./®3ˆkĢm½Ć—u˜÷Ž€JbŸHméā땼Ub¢S·&Üziٟf³ńĆyĶŽ om„8ł(ßf×5µ³ĮĮĶLßķFĒĄQR[}Baf-. Æ‚¢łg0&d3%ėĻļ·:ŽĶ‡ƒBÕZĄU?Š‚¢Øų†ŸyŚŽC,5“/–l`ĻŃ"4ļPZtĄUną²6ĀČJżļŻĄ5ŸÅņüĻ0Ō»ö£Ė—ż›Į’Īę¶/ßį†šÄÄ×Äדo÷n>~äö];›īg`5(Ł3ŸWߘòiø,„5£ćˆ[˜üĻž׫‚Üģüš ^ÜĒ’y•.”‡·{7Ɯužž0ŸÆļĶ ö³üÅįxŸÕ»·sŃ&āTģūq‹÷—į6@Q­Ųż—@·nm‰š®GMkŁlX“ˆÜĪ㉠²JŪ!„B!„¢įS| ˆŠ "ĄDå±<ʼāh?`0"³ŁłćęŖcl£/ŠÅ7e1ß&ePl²c7蚆ӭ”¤9¦-mC ÜGv²= œ¾¤WĘÓ5.ž„ŒJB"͘‹ŅO͊;…‚¢fč š7A^%łÉ»ŲŃ:žKū]ĢhēVēÅŠqpā“ü¼;ķų¹•Åł”RŒźpuŸgĪ]Gjś z Ȱ•øVķ'] „eÆ$xU’vɲŁlU{ryQ\‰™Å›s)ńmMĮ]‰wmaÅöCõØprG9nÆ(¢¢¢ˆšžįVĀäŹ%ūæ§"g—§ŠŹļ`š nEQNŚ„km`śņYf #®¾™“ĮT˜Āšs˜zĖ 6Oy›'‡üƒĄ*±­āńvīeߍ”­«Z@Ūˇ÷=Ē†ĪšŚ„„ŖYŌż)8:Ń:Tµc§½ĖŲs˜£ąg¦Ž3ua—rĖ#·ÓĢ_§ u7»T_üź[1z›·d6ųq®Šī÷7.yNēŠNey9zxw®č‹Esā(Hgdž|•VĀ?Ęõ&BŽ™ !„B!„ā/§¶ Öt»ä"ś6õĀģČ!mūJ~œ½Ž¤Ļ'Ö®ø«éÜ-ŽžāhÜ„½Æ®:U/ eéxåńŌ 5” ŃčYģŁ}„"CA-ŁĆĘ5\z1ļ†āĶß3oŻĪS³bdr,7W³Ę„4 ‚CyØ%{X÷½Få€aŒŗņ>ś› IOZĢ¢eIlĪüż³ļ”/#儤ź>ļ‡×~ĒW1ä*ī䇗»€Ģ=kłłĒ•Õ֔Z¾]Kā,ĈA·šąeVŌ’#ģ]’3ß-ßĀΜśŌ·FyŹÖE†såČ{xŗēÆÜųļO9jćOŪšķŲ…Q7ś±š±÷O»U·}ś:ī®ķø|ÄŻLUBÖ¾t JsjL’÷€PĀ ŌāœóŅėäc6! “'š¦ƒbƍ¢šĄbG5YP¬jU ×0~ īžö殌f`č:č†b`ØUóļ£x~LÜNW½¦$5Śifv³>Żu|­t;2Ü\g#R-aæQÓ¹6š›Ż$qÖėū3§{<€^ģ$ū™ēµ Ōa5ÓČ ž9³Żä9 ­MEŌq•Š!͘7ä„üšĄé£¢ \—5N-”Ь{O4ušģWyģ?ķž”Ö,ˆ‰]}ihʦiTš BmS+ė•Å8‹3ŖŻgńnŒŪUq©–³ńŻē˜u4{Ž}™ -lUŪū1ōā!“}čŸ<ūĀkōļśCüޱģÕ§ye2‡³ ©Tż‰n7€±·ŻĪŲö'Œvg±źĆWy÷ĒM$ēh4NĄ„ūļć ¾(zKgLå½5)¤gRŽ7MZõeü}’āŚv~'µ«%¾Ķ”eģ=P‚Ń:Šö’ĀwI{IĶXĘŽń “5FÉū3PćÆ!Ž č)¼3~ß÷~‡¹w¶© Ž»ŁöŹ•tzĄŹg—šŅ`ƒģždō·Łd9,א÷Ķl. eŌ“‡¹”SÕK{ŸŒ:©Ž±~ę˼6w-ūó‚Zōęź;ļaB·ąŖŗ©¤¢RēČg’¤Ėgž·m’ļs>½¶ŗü-āŖ ×p׎ŃĢśģVZ™ēzžõ i·ĢåŻ«BP1(üī_ Éʔ…ĻŠś§ÉÜóĪÆ-rbˆ„ۘ’ćß·ö¦‰ čy¬~ļEŽ[ŗƒäŒBœ–`.zš¦]ŒZ[{ÕŚ!ėhKĆĮ¾ł/óÜĒĖŲ•U%0šwNē™Kë꣚6j%un-åõ{&³Ž *–8’J>œæ›]=ˆˆ1^ĀĮ¤U¬ß›Ī±RļŠętėߏ”æ=žu2V~Ģō•ž~ŚāāI\Ö<“„Ķ'·óxĘun„Å[˜óÉfBĘL`pd%‡Ö’ĀśäLŽU ™¼‰æh,—“68°r ėåQTZŽ ¾!MéŅ ĆmŅXB!„B!„8{ĘQ²·eęÖ̬įĖ”włł}ų¹qHEĻfļ3Łė¹WµÕEŁĮĢuók=[£ųH*©‚h æęy²XøŸMó÷³iži­Žó”ŹÆū’·×}Y’DĖ3H^žÉĖ?«ńŸ»–ŸO,æ~„­ß¾ĻÖć×=ʾ%ļńģ’ßĻY’ĢŲ惫ŠWmž IDAT>Ļ‚•° šō ēžבMSĮzö.YĻsKŖß’ĒōO¾o# &šHS%ł‡SĻK·“° YUŠŻšg„n€”cœōcüįžémGQŃ4ó=w¦¢xĘ&kśł9<ƒ˜uŌ³(‹[3ąÄ4 ·ŠRUE `øY¾ō(_fŸA®ph§ŅõR'Ł.H¶aĮĮ‰3D›-&½Õߟ|OcaP5(GF ļŹaƊJ #†„ҹ”¼ŃœųDtö|a”j½ßEŸæ€³¢„jā‹īˆ:¹ĶUŗw~PT}¢™æ8‹rM‹?c,QŒłē(>½ł+ęÆ*bšÅÅ$oŲLv³[yöįÖŲ*2HśęC¦ß•LÉ»osk++PĪ–7īåŸĀ™x’ <ڤ˜õ½Č ¼Jė“éėUBŹę­ä5æēmµ<•Ÿ¼ÉKX‰ūj2}N˜WXm’@b°Ę’]p_Ś É+VQŲ{8½¶Æä罓h›hķ»ökČjM#„¦67Óźśi<}i(**¾aæ—Õ7ˆŪĒu”‰’ĶŹ™ÆńŅ#Ɲ’Sx4Q¦c¬ūq-™‰ż ?eiŌJv¼}/wĻQyēSÜŪĢąĄ¢wxķžū©xē}īhóŪ *‘£ŸfĘ5Ķ0”`Š@%·šüłįc“Ćśż.v“j¬ „ngG‘›œ{q^‚»·ļĮhóO:y+x·Ć=SÆ„‰ŸAΆY¼šöSLoł5Ļ ņC”]+W“} Ļ<Ō­SlcŌŗŚĖ§¶Y{[öʘĶć/®”É-OņAÆōüTJCƒNI^}›ÕZq&LvVÜøŻž?VŽ®™ĻĀż~tė)ƒ}+Hݰœ_®Ęü`šš<ż“I—KŃĘ›_}ŽĪ:Č:xˆ¢€žŒ¼(»^čBĒŅāźĶћ`rrpćZV|æšĘć'Ė !„B!„ā/F-ŚĻ®”š%“„ć!Sńæ¤%”E–ņ½ģN.„ó0æ„€…h€ĀYŠ 5ń^ĄõLė|āOuŪōćć“·|¼¢ØčŗŖQgX+Øą;€Ä( j–gZeT m#ĢT«äØNihEN2ŒĀĶ(Yu8>ŻćĻe^kM·°’4-€fµ·£“]–ŌčÓŖ1ƶ•³° †’:•nš¶©Ø€VG²Ö`;±J9o¬/fk hdTŠ Ą>Błęƒ)žØł LŖŹ„{1\eč.†«½j=jŻķ@w–ńż'SjLWĻM%µĢD‹6-©.cŽoC+›“}‡3Šńč|›v”_ĻČŚ^½Z Üp ³f­ēŗ)żń.^Į'óréżšŪLģµŪśįLÖ\ńK¶>@ß^Uå‰ėDŸnm0хn!™¬»łGVķuÓ§ó /›ęthcå˽{ČŠ»kģcń²|zLŗž–ykénīHl)k/{ |IlWėTŻö ā›GżžRÆ(4Jčψ¾žņtJgĶM?œ’5źJžóčAž£W¶¢’ČK¹ņŠ‹éåU5r%3æN„åMŸņŸ«ćPn¢)K¹™Ÿ­ę†gńŪją–ĄHā›7’=Æ®źógtźN;ŽbÓö ®h#wĖV2||aūföŗūŃQ9Ą†­„Ä_Ņ…`UAļÉEUē&¶ņēĄŅX°#mPŪŖ7# ¾Ķ»Ńæ[›ć×6źjÆ>uGējjĖz>…†½:w”}+/ UŻmųÖYQ_†»’Ņ‚£ģ\»›BƦō 7AÅ6ķ(%vŠ•ōjį@“‹J8ōńÆģĻHÓčŖ‡ŸO ĮA~JןsE[P4Ķ¢CoSĆ³ŻŚ8’øčPT¢ˆö-&uĪ^ęčÄEÉŠn!„B!„BüÕ8Čܾš_ŽŁ„*ž$ éänŻĖŠŠ,RJĪĻēOņY„ PZ“®ŗŪ3]óo#wõjF÷źg8Ms×ėK%Fe)s·;y®[8w¹Ž±,O!.!ˆ«ƒœ|»¼ōųšŗÕžė(eQruēVW«ó!¬©q*l/_īJĀĮLī]į ¼ŽćĻg^kM·¼”ow5fJ—pŌóYšįĀe±cw²tOå,:IksYĘ­WEÓlK!I9n*­‚NØtŻEŹ1ƒĖZ5ęŅģ"R1ć_^ĘŖ¬źóįŹÆ$ƒFt÷'o%Eŗ™Š2Cé-oU?õó›żPT3ŽŌUč®S[ĄdóµöŒė÷E€޲¶¤w— >Łø‡#ZZ¤īē@¹ƒ£SGŃżé㠁ۄb;VVm*jD4‘J…ÅšīE»-ą­ķģ,5ˆ8ōK‹zp_Ļ0ŗø»šÜ‹Łr{;÷ģ YMąņĖY׳EDµy±Ņō’Ē˜=ųf¶-’‘łó?徫?¤ŻĶĻņŅMšNŻĖ¾Š&\Ōł„`¦)š®Bx{ĶRµA$žę—#” ōm=ƒĻ’v抂MŅöŗššµ„¤TöÖĶľ¦OļhTœ¤ÆųˆW>YʶŌcTXż±”i˜Ū9k½†Vg{YOė;'¶„¹÷Lģ¶‚—žļZv ĆÕW]ĘąVu¬§}få's§üČ«/’XՑT|ĀŪ2tLZxž™K®ŪIń’xł„ést,e’›zVżšW*(Æ4¤±„B!„Bń—¤–„‘’,õš§ÕĮVœßk4čpAAÆæž:ŖŖr×]wįļļ/½Bü-˜UM×<£)ėõ[żHąŚ‚Ą†¦aŌ+e°wŻQ¦øCø”K8S½”0·Œ/äņuVczIZ~”×]!ŒéĪH»AF– Ķš T8yµÜŗ?y­ĪöUéL-įŚ6!LÓĶ”äc¬Ż[Ał)壓„ésÜģé֘aķBč«bŅt J\l9XA¾į)ļź59“ĢõūbvŗŲø¾‚Õ5€µÜ^Znę¶®!<ŃфŖé—V²«X£A‡) CwU»K1ŁP”𿉠Ēć­±i÷~œ£ŗńĒx·;eɕ¢c#PÉÆžŖāyœO4ˆ‹Ÿx…›[ŸźSńn„RMŠÉ„ ½š©ÉUB»t%Ī=ŸĶ{Ji¼ģ½¤§ŠWÆĮō|a “nEݶwĖ«éŅčģ'VԚņRµßFĒč8b,ć>¾—‰ļLć³~Ÿrėłč!j8}ś6ēµkŲSRƚQōžæaųlśˆļ“2øŌ¼–ä°Ž<ÜĢ„žņ)=ö%ź˜ū˜ś@Aįė§ē—z]§¶ö:Ķś;±-­Ķ¹ęåÆč““ˆ9³fóŸfńłķÆšĪ„jś^…žņł™—CgŠīĆÕżć0»²ŲšĆrR­ĮDY?£ųŠzčzœ“°²‚ŧ–o„*žiüõÓYC ę*ĘIOB!„B!„B\Htųƒ>dūvĻøæY³fqūķ·’ķH×uŽyēš7oΰaĆ¤ĒžM„7² e»įÄĄµŽī=u[ķĮaĶ`®oPČp³%)“-I5ķÆdöēÉĢ®n—³’Ÿ~Iē§Ŗ‰Śø13®mD”Ć3²vĘGź}|µ×©,fŹėÅg•WׁL.ż¤L0gv2sNz0ŗŁ””ɦźŅ­¦ŗ£œE+޲hEĶUė.,ęõ/‹yżŪ«ÆOƒ;³yhgöļY5t ·«~³ŸžI t¼›å·@yՏ¢ (*>į5ŸģՕQC›šćāłņßģ„)ŻGłīƒ¤ś÷å¶¾ÕoŃÓŁ¶=[óx¢M`ŠŽ§™uūčDhŽåŌ*>-¦¦Żčü k—JÉrüt7¼üūpIæé<1ŽéłDõļJDMqnņŻ%%eē0LėE|ĻN„¾żG24LhiūŠĶ›¢·‹õŒÖŅŲ“5{‹ÖĚj){łS‰xńļ-䇣łlhܝ£Bšéόå ųĘ“›°wŅŚ •öpĄčĄc·^BtbüėßÖŁ^gKń"¦ĒU<Ųㆾ2~¹€­×%Š£†2׷ƾ±bčI<©Ś­~„„c!˜”# ™=o5‹7‡ó®Į˜‚ 2m#·Š QėjFdėf,fجØÄą„Yż/|¼ ø 7ē¾Æ!„B!„Bqi°ବ,’’~=~ÕŖÕÜpĆ ųųųœók†ARRžžž$$$4ØzŠ47Žxƒ›nŗYĄ#ił•$n@­!€{Ó>Ÿų£9Ńu·'ąc:Ÿ%Qˆ µā$£TC·ŁčŁ5˜¢"ŽŹ5ĪĮńāBP“ē;œy{ń}U~ƒWp+ŠrŅj9Ū›ž·=Āøķ0cŅ­ģ{‚Q RX³`ßīņbēw14P© `źdƘÅūŃCi ‡ĄūÉŒ¹»gŪ€\ŁLīųō15Mdt‡P¬٤E1śāvœö+Œ%‘½ųāŪĻɌĖūmĒóŻė’Axß;‹Æ“p®›Ü¼ę‡š)†„x+Ÿ/žÉģ¶WŅĀȤ °?ĆėŸ ēŽYLł®œ][hG/:ĢŖ9ßrŲŽŽk[™Qüūqć?błēG’fŠ÷­\Ś ’½Ķ›rŻC}Æ’{Złkē3ˆį­ŽēµOrˆ’.ń&„’Z¼õ3&ŽÓ3 Ä5'Ęų’¹ü@“”ĶilŹ!£“Ļ uµ×Y ŖÖÓWóÕ&ƒųMšrå°į` ųŠHŌźĖ<¤®r(ō7ČÜų3kÓ¢éł‡ūŃ>(ņ”pkdO†wIcNŅr¶4»‚®›Ó%q#ßlśžļÕī“šĆä*įXE#Ś&„cUilfó¾l kO0Å8¼šŃ:<f̓X·q KC4BģP”G¹¼t!„B!„Bˆæ”žłēŸ=SÕVq¹œ,_¾‚K.¹ųŒŅŪ¼y3O>łIII8N¢££;öjyäÜn7×_?ž»īśæO&Õ3śŪPŌF÷ž0²WÆf:h½ši¢OŲfhžu§„œÆ„J“p_.oi%ÄKEu¹9œ^Ä KóŲ«‹ćŅ@«,ĘY\żĮļĘø]µžÆōäĮ÷> ż'ńŏońųG%hö`ā;āŃ·'rE»Ą–³V°YŠX’ń3¼ŸéÄ/¶ W?s?wtńŖŚļC·{^åå€×yū»éüė½2š „åą;<ņ ĄXé4°Aó¾#pųHOvhė8’įaóųDėĆE­ky¹U1ō®Łōä[¼łšrÜ>Ńō™”Ȱz€ \ÖFų,擗>#£°Õ;ˆŲ6xąåŪø2\쓿mÆŲ_įµ™’įöjћ›§ßĖÄÄ:’®)ķüQŌHFŽźÄ[{ņ:ø…'ČyCŪ¼Ķn÷%Œjį {›Zē™‡ŽńüĢÜóE)šÅ›FA1“ņ«# ZG{ÅS—+’?śÓӋpšżˆJčĒO\G+@ e¾ŖŽrØį\róµ¬x~Óēõ§×Żm’pæ]ƞzåOz+Öu’ēņėź½“Նč~c浆µ»V°`½l~„Ä÷”Ek°*vZö½ˆōŸÖ°fQ ŗ5€¦½Āhn'¤ĖFT,gmŅböV˜l>4 ‹¦‰·*Õ,„B!„B!žV””’˜Te5ŖžU­jpŅz”ŗ”cčžūŗ®ačŗ'@„ėčŗFIį16®śńŒ2įp8˜<ł1ŽMĄb±¢ėšV{Ä'88˜§Ÿ~š   Z;vģ]»v#&&†;īøĄĄ@’““1™LÜ~ūķ8NŪr×]’ĒŻw߯ ČårŃ©S'nŗéfīæ’>é± ČŹ•Ėy}ĖsŸ°aн©7 {oaĢ’Ę]’Š”kŗŪóŪŠŖīד­jū)Ū4 Ćs¼Å/šåĖWš•}*•ę óēŪoS@—d`õ‹—7øü½9яK®ūf“E5UĘóE„ŖiČĶ~…[ßĶ=ū‹é)¼3~ß÷~‡¹w¶9æƒŪ…¢Jßēįb¶Ōŗ¦yuμ æ€`TՄ¢ŖØŖZõŪ„¢(žŪŠZ5e¾gŚ|EĮsæj&…ß&ĀVäµ\!„B!N±ū׋Œ”Šē]dDÅÅ%”ėŗTˆ8+ŠŖ¢Ј’ҲބM‘§•FƒˆR\\|<ų žŃ¾õqģŲ1222ź ļŚµ‹¢¢"žž9z÷ī ĄšįĆO9nņäǘ<ł1fĪü˜+Ƽ’>ųgžy†üü|‚‚‚˜0įF&OžŒÉdB×už}öYęĪGjj*V«•—^ś/ćʍ«vūµ×^‹ĖåāÅ_ä‹/ꐙ™IĒŽ™6ķy:wī @EES¦LeöģŁ”––ҵkW „·’Éu`ą –Õŗžoõküžqū)ÓAė. tMĆ0Uė² qīŁżƒ™’ń³5ÉĄ00YlRQB!„B!„Bˆ –£¼«Õ‚ŸŸ/EEÅR!⬘Mf||¼±Z-gžFC(HXXżśõcÕŖU§u^tt mŚ“©óø˜˜Ģf3sęĢ”K—.ŲlÕī¹ē®»īŚŖ“£čٳ|š>5bŊ•L:•öķŪ3zōh Ćąūļ yóf̘1·ŪM||‹·<łäS|żõ×L›ö<‘ü÷æ/rĶ5ײqć|}}yä‘GłņĖ/yā‰ĒiŻŗ5ėÖ­gćʍŅŪ’N U1ŖnjõXē·īµ’64—'}Żķ cČ(`q^ÜōŹ!©!„B!„B!Ä_ZaQv› ???B‚K…ˆ³¢N—‹’’’3N£Į,Ewė­·’––ĪįĆõ ńč£`2Õ=ÉgÓ¦My啗yųįGX“č{ʍĖĶ7ßLÓ¦MO:.4“É)åÄÄD= 1v芁¹sē’”“Ń£G?¦M›6 0ąųżß¦®žćö‚‚>üšCf̘Θ1c˜>}::tdķŚµtķŚ•Ł³góųć’ę¶Ūn OŸ>̚õ™ōöæ‹ŖuÆĆüMc€ī®ēčŽŗƒĄFÕ4ī†ī]G7œ†~ŚSV Ń ©Ķ™4k“¤&„B!„B!„’C†®S^^Nyy¹T†hLŌĒn·3yņ£×ylhh(S§N%$$¤Žé_{ķµģŪ·—gŸ}†Ķ›·Š„KW¦M{Į³Ęq-.\Đ!Cˆ‰‰„uė’““©ØØ8£2&'ĄįppĒwLPP0:tÄét’••́)ø\.zōč)=óożB”q8»ø*`ė®!°«Ÿ<ŗWÆf:h½š€1U#‹µŖ) ]•žµ1¤ā…B!„B!„ā/ĢģL-\ĄO šÓwóųī³·xgņU ‹śż˜‘’Å—eŻd!.tꆔ™ĄĄ@:vģČŅ„Kk=®S§Ī§üż··7W_}5W_}5/½4§Ÿ~š‘#GŠŗuk”j¦æŻ½{7'NäĘoä…^@Q&MŗķŒĖgŖŖņÖ[oŅ©S§“ö………±wļ>Y üoĪ0tLh”Wh8‹Ņ1ū4ńiu÷ļæ ĶÄ=¾ż„}ŗĖó[ól×uhnŠŻ(†ŽŽĖQŗ†S7a2$ų+„B!„B!„öķ/pÓ3›9† {h½®æ›žPqÜ;›Õ©׳*­D*Jˆ \ƒ †Į¦M›ėĘą3Y½Ķ‹Ż_¼Ęn©&!.x *œ’’BAA~Ē°wļŽSÖė­IRŅ>ūģSś÷ļOHHłłł¼łę[xyyѾ}Ģf3mŪ¶żöī<ΦśćųėÜ{ēĪbŒ1«}4f;3„±DE)!æØ“Pi/“‡“ØP”в$$Ń*)-v…”Č6 ĘĪ`f,³Üåœß3¶2[ļēĆ}ø÷Üļ9ē{>ēÜsīÜĻł~æL›6zõźcYaaaT®\˲2ä5:uź„ĆaēĄg¼}”””Ü~ūķ¼łę[Ųķ5jČįĆY¤§ļ§[·nsļ½½6lMš4ꊔĆ:tXGźåt¶;Ų~ĄdSé»yk÷a,Ó{tląs³ưaō§X±`lv ž°cĒADDDDDDDDD Ķpåė=HvVŽoč^œÅ=KŪsēW^Zæö>M’ęs°^K®ŒņĆŲ±˜Y£ßeܟn,Ąr”„f·ūéÕ<†(æ½lłu1›ŖT'ė•Ēyo›S¹@Š\ų„Ź9“oß___fĢų·Ūuō½Ķ›S öõu’––Fæ~żŁ»w/$$$0cĘt¢¢*šā‹ƒčŻ»7Ż»w§xńā<÷ܳÜ}÷Ż :”aƆńī»ļāt:‰ŒŒ :śŠ3ŽĘ—^Lhh(<‘W^y…   n¼±#·Ür †ašĢ3Ļ˜10dČ|}}‰‰‰”J•*:Z/‹«­aŲńõ÷'øbÅCNK™2e);ŽąJÄߌ«6ēń¾'-eŁ£ØSó[F¾÷(cö†P”˳ y¢3)÷MdnN1ŹöxWk}Åȗ^ć×ĆåØvóćō+»ļ`‘ ŖH%€+UŖ„ĆįĄćńŸĄm·õ lټĮĘ7nĢųńHJZŽĆį jÕøB/·vķŚL™2å_×}²±‡{ö¼ƒž=ļ8łéŃngŃ¢……žąććC’žżčßæ_Ė|ąxątt^ĘJ—šaė~—!""""""""""ēLvŻ˜ōuž ĖĖĮßF2ą„ŸŁąµ0‡›s§óć_,2X9y&?_ӄ„ņcłiWn¹&¹?böF'ÉüńߐŲ"^¹ĄŠT8&&†Q£Faš&”””'¼WŗtižzźIŅŅŅp8g5ÆHQ·5Ż…Żfą5-CDDDDDDDDDDĪ ’•/Ó}ŠröJŻ~cäŽÉ¦öBĻoĖÉ =Ē—P‡…§l q¶U|¹ńøT“~Ņ)E­B’–Ų ×^“KŽĪ 75Źśą“±rkÕJūćtēķy•H?ü6VoϦRøÆžŸå󊔾śŁX»3‡ņ%ēõy¹`_üķN~K·ėƒ#"""""""""§f¹q»Żx¬],ž>—Ō—nćQóöęĀŽ×{\†×°a³9pŲ”õ)j HŃ“z{öŃē+·f×ē«Ž[—žŸżó5;’Ėēn,›Ć.Æ>4"""""""""RhŽuŸ0iķū<}sM&]ĻžÓĢćŚw„’ĀÕŌ‰3łr„؈%6…@DDDDDDDDDDDä2cmcīō_Ł“Ųƒ®å]§=»#ż;>›N£{ļå¦*A¾‚:ZRß®Į"šZ‹œ…±·)rA™¦‰ŪķfƆ@ć‚}QĪ`ÅōIüZ²#½Z–=’wVż×ėūOä]ü4źmVčÅ3×WŠŻirQŠ&Ź•ƒšńńĮf;½£·łÅODDDDDD.,cåd&mĮ£7WēŻaɧ9÷֏y’ļ¼ŪžkĖŽ­ü¹h [Ķb XäSXä,DF–Rä‚2M—ĖÅŽ½ūŒsͳŠ‘=žąĒÆ0õ‘x|­,¶ÆZĮÖĄ:4ˆ.†q¤œµß>’„Y‰-ø§eŁs·ž’z}’•ĪźysYqu¼?žūóĒs¹¹¬…„„‰Óé<ķ°ˆˆˆˆˆˆČéĄŌŪ¹zź‰Ó ó/¾üZ¾Ļ½ų¹v,>īż9w`Īq©$Ćót’ šĄ–“ĢĀQ±pTŽūž°Ūx¹uig øČ¤_©DD䜱²6ńƘÜÕå:7”AėNtļ7‚/Öd`^™lӝ„«žį‡¬/żÓ³$&ŽĆGŪL°#¢B#ó.–ž5ŒņIF-J?ė; §sø¾³ŖėžY<ڲīŸĪĪ’j'ž=ög£ 8žĖuˆˆˆˆˆˆˆˆHžŠŃTŽæžuŌžPäBŅ'PDDĪ +s)ĆśōåćhŪå.:Ē…cĻHaįWSyńž¹$ z-Ć’ćd›Ø*1øÖ±~‹—Öqö¼ÉŽuŒ}ō–Ę?ÉČ;Ŗę_ ½¤nHĮ\—øHŲ¢é:d4]/‹8Ädć—SYč”ŲŹ©|ņg­éüvŪł‰ż¾‘Kœ»\3n,»‰„«÷p dnī^…¬Yo°Ņ£Ą"’Ą""rd³lō+LŚ^•‡GæÉ±G:ŌmJėk[Q£ßݼüŚHšÕH«ā{łiÄ`>˜—ĢęŻ䦂(_ó*ŗŽ{]kK|zv1ģF·œä=^BŖ^ŏ=J突ęę ‘1 SŲ¶;ƒlˆØŅ„>Ī’jēųƗ>1Uˆ6~bŻ_±ā‚1ļ†Ÿłś·u¤īų‰u=ŖRĆX‡Iް[L7b€™Āū=īąŪÄ÷™Ń§y©cææÕ‰ŗo8iõņ m `±{Önųb7»²|+ .§§€³Xß)c·c^g̜U$ļČĄåF‹¾2äŗ°&³—1eĘ6źß;œfsbŌ' ø³ĘՔ8²QfŚæļĖPņ # IDATĀ”ł»“ÅŽÜĒŅG0ā³…¬ßēĮ?¢*·|‹^µ¤Īxš‡ß’•ķ™.ĮQŌļųĻöJ$āŲĮt’8n?É:ö²dĀ›Œœ±ˆ ū BcéŅēa›Ó8īDDDDDDDD.ŽĄ(Ŗw½“Ūūścß—ĢŖ™óųׇš(4"ö³©ˆˆČYĖYʗ³wŽö)ŗÅžm4UŸrt¼»ļšĘ—ó3iyķ’—&±;ŗ/÷Ć7gæM˰“98ś=zUq٬xēžų¾4={§"°dÜė¼öÄŹ|ś4Mü’’“’}•ī啧āpfoeŽG£ś¤“ŠÓž¦qĄ±ÕŪ"ŖR=ĢĖž…§}=|š’‰mhōĒ<~\כÕąż‹?7x©Š!./Įiü²Y„ū·Ä†ĄRǶկāÕÜwKĘnęMÉŠ'Gž£.§§6gŗ¾‹]Ī[Ą¶ņ÷šRæŚyb 9I"Ö"}ī ~ %ƶ©AĶŠŒ~n:ßķjN×ŅGJf_¦ĢæÉåĻ1ņąd-ļyš>ՂpļĶ xY`Z«#æų?"Š[ģY:‰×ŽȰʟńŹÕG’²Ēńųu¬zļšjŠ®Ļ@‰¶ųkęūŒ|ģ1rŽ’€ū«9Āw""""""""—cŻD†<6Q)b”‘³f¦„’zŲNlµŹœ,ēˆ©F_ė7ļĄ$/łxEMäµ¼lŌ(ć¶{˜4i ·jFĄ¹|ōy‰żß£w˼V»qżw²š¦łaå4i”·ÜbėŅø~5ģ$P?|'‹ļśŽłė<4Ž?īņꈄv5'Ÿ®[˳QÖzf’“Ÿ½ļ„™Ļķ¼;g ÷Wƅ}×:Ö¦R½fÅüÖ¾'ēZ˜JåŽ%LMƒU›Ń¶IŽöćncį³žQ—Ó‹Ó™­/1¦0±3¬TŸfõ«¼­ęfĪXBHŪQŌ0p4ģȵ”}˜žu 7÷Š=a¾SķĖb§Q¦@ē3žÓMÄÜ9‘AŻ’¹cŅ"’yõ*Aü5ē6¾Z•Š÷źGæčœ<ŽĒXę1į³T*ß9‘ŗTÄŌÆ[žĆ)·1įćÜöņÕrĒˆˆˆˆˆˆˆˆˆČbSDDä\°Ī¦”³2‰ ”^æ–-^š¦nąÆģ,ę¾Ų+7„~ć¦4øé-’ÜY¤ķ=|Ņ„ŲŹ”§¬‘Iʁæeöš§fķXHłƒÕ‡,Ü«ægNf®iXŠĘW'ńćlVø,²×®"ŁV•ŗU}ĪžāZ¦eNZ—³ŒS!Öw&±;oŹ,¾ZM‡ėāņ’ØĪtlĶęY3łĆuŠ’¶/ĻøĢq<›×².;‚ųųr'IX»Ų6÷}śŽÕ•kZµ¤YūĒų$ՋĖå:­zS×±>'oGæŁĖSÆn8Ł֒ź-(öw""""""""""†šŖˆˆČY³…U B€—åk6ąźPŸæw°ėIYKr®å£Ź`c’I—aŲ °ŽKOŚB¹öł·ø+īų”Ÿ€°'Y†a·cĒÄū<œČ„zTō|IŅŚC„üō3Y‰}iX̆£–4|m8ßüÖ ŪļkšTīBB‰³ÉÕ°¼.§§”3_ßiĘīŸ\üńĶ,’s¶±¾s#Ž:a#öņÅŅŽŌmģŠśüm_ža™c .g¦L¦ß3Ÿbėų(/>Q•P¶šŁĄēųł“÷šufūŗĄćNDDDDDDDDDäĀP `‘KĢŽ¤)dn[”@ČĖæZG6{<Ÿnü[ĖKĻv¾žš+RƒšŠ”I0'MƚŪųż}ųVŠ”¼ģåcˆv¦³a‹I¹Š¹ā裑§é²_QŸśa™,ūe"Ÿ’-Æ­O`5ęŗ¦æ|9‰9Ė÷S®^=Ź“xĆ??8xšš¦ O3Ngø¾s»ģå|õCµī~›©“&ņé‘ĒĒ#¹§Ś~žf!™UźoūņtŹX¦UĄ6ÅRÉgIIŪų{CÜÜæÖņ—U›Ī½®£AÕJÄT©J… ć“÷›=Ŗ*•}÷”“żXļŠŽ­,_™†_lQv}ĢEDDDDDDDDäā Ą"—ĖdóœĮ„×ķʁ½)”­q=6‡Sq‘’@ ļ}’[žx’į½{±¾ėM4Ɔ-=……_Må‹?żi;ąAZ—4ņĒ^5Ł=w”oM­HŲ4ūC>H.CLJē³|ŻÆŸĄżŸį){On؉3g7)™åøįښ’>fģßłTēŖÄ`>łb2;Ėtåƒ:¾GėŻčŗ« xdÓ¼„¹õéJ‰kÆ@Õ'“gO`JNÄZ;I/ٌ6ÕĻSœ¬3[Ÿqbwč×ļųé@uī½>øŅĒ'MеŖŹ„ŃßņszK:b_Rˆ2F JYģ\ö#‹¶–§IŁ“lSĒńÜ÷į“€łĀ©żæ±än]€Q"ŠŒC¹–ŖAd•–†z}æŌ˜¦‰Ėåb͚5ÄĒĒ_$•NįżwšmāūĢčS õ¬{1€…Ų—Śßr‰KJJ¢Zµj8Nl¶Ó»ĪVoBńą0l6;†Ķ†ĶfĖ’ßŽayĻ FŽkƆa÷ƒü»ņ¾Ō†v†ˆˆˆˆˆČ߬łu„Ź–U D䢶kūvŖ5hwZóذČEÄ2=Ś·‰ƒ»×spĻz,o.ł"I[’ń·}’WȰazˆæq˜ż[V(ø""""""""""""— %€Eа}[’(ī——¼5-l>`÷Ćpųa8üĮQ L/9é›ČݟLdLCö'’ȦÅń؊csĻKŪl† »ė{·,UpEDDDDDDDDDDD.AźZ¤Č²Ų·%‰ !XXv_°ūbųųcsa8±łba‚eāN[Ķ”ĆNüœЇE‘{8ŌÅcqW DŁ:ų^ ›ƓMī4rļĒ·XˆĀ|É.YЁˆˆˆˆˆˆˆˆˆˆˆ(,RTŚ»ĆrasųaŲ|0œA¾%0ĪĪf’¦ßČŲü+ž}«±ł–ÄY›ĮÅ š=ŗ «ś5lŪ°Œ)‹q†Õ&$Š‡į”øĆž­+(×R>¬ŻŒnEļļs’ö†/m?ŲŹ·w…cœėuzždĒŽhÅ^DDDDDDDDDDä2§°Hµ7u9%ü}°°±’ö¦oe’®ĶdļŻ@1kvEß¾żŲ“i³ö–\VŅ6.Į—V­XÄģļf±zéĻģŁžŁvR²+²!«2Ū\åČ2Ā0ńĮĎײc>X†Ėp`Zv¼€…ƒ\{‡²=¬Xæ |CHßµÓė.\e¼;˜ór7D•ĄĻ/˜Š žĒ«?ķÄ{ä}s³žļDćŖe(įēÄ·D]?ځ `īfް;hŠæÓI±Š(j5ėˆ$ĻÉ×uŖņę6¦?ܚųŲŅūūąć_’ŠzšÖ’ ¬B×51„(įļƒ(UŚöeŌūĻrKb,įÅü)^¦_[Ą~ėøz¹S™9  bĆ !ŗé¼»,ėT'ؐŹ4jŅ„&G‰Ttœ"^^6¼ß‰Ŗ„‚šóqR¼t-®nŪ½Ēļ‹]üüZw]Qæ@"+·ą•%¹śĄˆˆˆˆˆˆˆˆˆČ)uĄĢo¾āūæ=~~»ŻŃ2ķŽ˜Ä§=Ė}}툯Žō°‚'—•»īøż¢ß†"ŁŲ²,ęĢ™Ćųńp¹ņO?ż7Žx#;wĘ0 }rÉĖĶŹ$¼NsĀbL“šby=€6? ‡/؜_ »Ćęv'†aĒ2lX¦Ė“CqOQž¼ž,pgįueaŲģŲæ+ §‰©I6æ>-׏4čöņD^­n±ś£ēy¦C{²YČĄś~`„±ōėoŲ3€ŽiBIO:öŹ‘ŲČféĄki÷ĘaŚ?÷_4ŒÄJJæ{&óė/Ä;ž±®S–ÆĪŸóę³»Ę`>~/æĆńĶkOńDē¾TY3†¶Å S×üeŌ~•©ĘćÜ’ļ<ö>Ļ=/¾Ź¤Įģž9ˆ‡ž¹­exS'p˜O_K—O¢č7üsŽ.·Ÿ^~€Gn|‚ŠŽ”]PA'3ĒƑT·aŲ°Ūm§ˆ—ČÄ^¼śńć” ¶ŲžÓ<ņÜmō­“̤NĮd³tŠutīę¦Ęšb½rv¤Qģ P4ł~¼Ę/%±÷ų‰^×ѧ©Ė–0ėAJ.k]oīĄ‡ć'\“ŪP䥙™™¼óĪ;¬X±€Ö­[c³ŁłžūŁL›6õė×óĄP²dÉB/Ó²,:v¼‘€¦L™rĀ{Ó§O§WÆŽ¬^½ŠŅ„K먖¢Ć0±ū•,L Ė“ƒéĪĮtĄ›“‹åuc™^,ĆĄĄ†ĒZ£Z&X¦åӛWĪtc™,ӋOń²øŻ¹`Y’žłI’Š7F­£Ö3+ż@v EÓX®Žē”ßšŲ'7“—B¶Q¢F+®»ŗž±KęL†Ž\M徿1ńÉŚ8kO oŚ&p(LyƒāqWѶe}“ yŁĶüų1ß&¹iS«°u5(^¹1­®ŖƒĘ”’k:3_«FĒ^øĘ $¼I7°`ĮF¼Mć°ķ’‚a£·sĶ;syžę0  ī;©|;ˆi ަݵ¾'ݜœ/{PҧDZnÕ§XüĒĖŌ3 ˆP¢fnؙ÷¼^Ż’¬ž4ńKÖįéŌŸŒÆycäŸŌxf%cĆ˜¦‰ĖåbĄ"""""""""ņ/ o6YYY.ą§į5ŸŒdĀ$rŃ'‹Tų÷ßēwF‘ž¾ŸāŃøļ¾ūØ_æõė×ćwFńĒšųćOœšžČ„Čė5Į°ƒåĮ°ŁĮĒ›ŻĆ骟ȵ¬#ÉŻ#Ož4 Ė4Į“° Ėfę'ˆ¼‡ŻŪUˆzxÖ/ē÷¬ņÜpU%ģGϱ\Õ“,æ]ĘĻĶŌ/ Q¾gŻRV.GūÖÕpf]§YĄ^1†Š¶}ģŻoža]ķ”.WćPiY€p”¢l$ĢĖČėāŁ³a%«dó]åšæūČ|&— ’°?éø¾Īꃙóz›£cž„©ź¼mM.æĢ“Æ}Ęāõ;Éņ Į÷ G£Üė–±āpŽöiw9×½8‹{–¶ēĪÆNž#¦é¬Hżī½¹£i4|v“ŗšcŽæ„õŁ6ĄN±„žō¹µ õ*ą“¾™Ōł£8ń/Ņ-ÅV.>søHŒģr¹;vƒ&=}?5kÖä7^?!Į[»vmŽxćuāć8xšÆ½6„‘#G’““sNėāõzyżõ7ØZµ””a4oޜ¹sē;¹™&}ūö£fĶZ„……NĒŽ7ņį‡ciٲ%‘ÄÄÄ2|ų›Xǵ®t»Ż¼üņĖŌŖU›ššZ·¾†¤¤$}z¤@¦Ē †-ƕ®ebš°žöśō¦cŲšz½…¬‰Å™^›-Æ/ć¼”0>80ńšg^W§†åÅkęĻm8qś˜f^āĖ[iž7v+W®ĢüĮź50ęę Ŗ­-ų źÖ«G½üGBõ²œźü³z(]»dKBĘ·€¹_½A§ØćNӖ‰¾'‰ˆˆˆˆˆˆˆˆČ™²lN|}}ńóóĖųāk/äĢF *÷B’šĻy’Ńtyō#¾/׏—{”Ą։‡śÖ$ą›ēxųŽžōü1Sļ"S?jŹE¬ėĶ.Ź1‹DxŠ A̚õķŃ×O=õ!!!’(ēOöĒfĖ«ö¼yó0``”×cš&999'<Ün÷ eĢŠ”Cy䑇łüóÄÅÅѹs–/_žwr“,.\H͚5łā‹Ļ7n,ÉÉÉ<’üótéŅ…iÓ>„sēĪ 8Å‹]0į# ČĢ™3)Y2˜nŻžĒĮƒźK_ 8^½Ž¼1}óøyŻ:’8Ł“¼Væ’œvbyưa™ö/UØķæ•łs7køźIfŽ‚ķŌN ņ)š¢:*U%Ö¾…Å‹·`b›O·ü¹¬ė)—[‹j¾{ų=ŁKt\qG•)WĀ~ĪöyĪŖe¬6›pļ€ŪiYÆ5źÖ#6Äv\=jSŻw+óē¦WųŲ—/_}hDä2’ ö É fńßŗYFDDDDDD¤9µŸbģgÓųź³Oó“x§]įREžŠÜŚōwf~øÕ.rӖ1}Źr7l€·d9¢»H]»™‡“‘śæ&:£ßzEŠ—ĖuŃÕ¹Hō"ŗ~żz*TˆbĒŽķx< £ąÖ†a`³å%Æ¢¢*’’ņW”×3sę·„……’cŗÆo^ā$##ƒŃ£ĒŠÆ_?z÷ī @bb"kÖ¬eĈ‘L˜0žč<±±14i’wRKIŁČšįĆéŁ³'N§“ 0uźT-ZLbb"éééŒ;–įƇѱcG† FķŚuX“hmړѧGžÉōäu×|¤å®y’Ö½ę¶ĘĄėõŖFÉėy¢OWæ| ½ѽŗÅŖžć•?«ńČŪķóĒŌ-`ވ¹ÆĖ@:½ŲGŸćę8ƒ3§ņ‡ZÖŽĻ¹ėŹ»XÜįS~{«Å’„üł¬ė)—֑Gīz…vÆwåVĒ3ōl\ßĆ[łs%īčшāūž¶gøßøšÄZ#3x"e»Ö ܱĶĒŻg„uäŃ»_”Ķ‹øĶz– ĖąHßDFŁė©dŗųy+‹ķ«V°5° ¢‹ač, r¹µ&sēv2œe‰ -ÄĄV»7¦°=6 t9 ’éśürŅĪąīiO© \į¬E!huäĻqG įöTœ›gņ隗xü­÷‰_:Ÿ_¾ŸÉĢߐ£°ĖElāä)Lœ<墫w‘HO›6 €nŻŗ0½[·nG»©8qāŃDķƾś Gį7”I“& 4č„i?’ü3ƽö6$“}4± ąp8HLLdöģŁ&§Ė–-ĆįƇÉĪĪĘétāp8ˆˆˆ 33€ääæČŹŹāžūūЧĻGēs¹\ģŚµ[Ÿ9)ÆéĆ(D«ß“·>UĖĀņz ŁB*€†ƒfņeĄć<3¤;m÷@dķkyź«aō»Ņ’Ō³”tł ļ<Įš·š~ŗ?±WVĄĒ°aĻæ©Ģ²Žė¶¹åĻ[]O)ˆÆĻaż8īnxJ”§ÖĶÆrS÷F’ūvœé ¹n_>z{ y”ėGdāuRŖ WV*™ŸČ(ĪUCfóeh?^x’~nz. Ÿš8:¾Ņ”‡j–(Ē­•µ™?Ēä–²n{ngŖÕ£M>ÜŻ(üŌŻNxÖ0žÉ'Y’æ)\©°ČåĮ½ŸäåKIڰ•=™Łxķ”Œ,O•„D”OgéĢ™¤Å÷ BØóœŸÜ»óÉgæćLģBēų£ē'×¶yLś|%ZÜĀ5‚t.ÉgxrÉõ&1§ļK|¼ßēŸļ»×óÓóŻIŖŃŠv­šŃęé›éńūK<üźrRMż…-Ÿ‹5ł E$\Æ×{t]Ė:ūĪüJ– ęŹ+ėŸ0mėÖ-g½\§Ó‰eYy]ź’×JŁĒĒÓeėŽN;urŲ›—`.lŹŅ^–ÖĻ|Bėg zæ&ĻÆČ곓})(^›;GżĄ£ņ·kķĖ4ˆGd„ #ģFĘmŗ±ŠåOŗžŻł2»ūYÕÕ·ÓT²:_¦*O/Ėęéćēó)OŪg'ÓöŁ“,óļŪaDŅkv½8ŻxRēž1Ģ»gLĮū§ž,öķÜJŽĒvNĻ®}›H>T’øØ`ŽtÜļSźJ®©ŸŹŌ%?’uõBķąŚĘŸž §āÕtŖ®äƈˆˆˆˆˆČń|¶n`§ 濘<Ē[@×Ī9d¬ž†)«æaŅē÷1tXGŚ”^ĀčķNP.*sņŠxųæV¹r,~~~,X°ąh¢Ųćń°xńbjÖ¬‰Ćį8Ś"łtÄÄTĀét’’’B×®]hłW–eįµ<€­€ītū|üĆėĀ4=Xęł%ŃßÓßa±Y™˜²%±güɗƽşWÜĮČ:>ē ¼9¬ó2ćS+qß{#čUõX‹ė«Ūäu}{/Żš(ė’7…ńŻĖaĢŸpgē©üÖē<];ļųż­NŌ} ĄI«—`hk?…Wä’ćaū’9,M%ńęŽ4Œ8vŽ©R#ļ‰w`²cŽx†ĶĖūŚ{mo®u±iÉĻ,IŽÉŽĢ¼öbZtåŗ*^UÉJ]Ź[j̱³“Čz­h˜:Å?® b§Zä,ž™ßs£iÓ¢*AŹžŠˆˆˆˆˆˆąrŁ)Z§įĘuųGfĢīĘčŪśrēŽQLłć¹”Õ©”̲”¼A “Ŗ—ĶÖß7²łP1Āb" uķbįA›)•‹=ł JŸ 88˜Ž½{ńśėÆXŒŖU«2yņdÖ®]ĖŠ”CĻx¹”””Ü~ūķ¼łę[Ųķ5jČįĆY¤§ļ§[·n§óX.OGZ”[†­€Ö½Ēµģ5OŅ“y’n¢›fyó,7ͼńy;­L6/™Į°OV³eĻAĢĄ²Ōh~7“?xŽF~ē ¼9Ėų|ÖB®y‚ŪŖžMwŪŖtĀąö‘Ų°XŹW±¹y¶²jŻŖ4'!āT7ų؈HhOŪjÅ10š-ī2Ųµq™Į i×¢ ~fFÉ ~=ģaŌkՐMS—šŻw;qmr{ĶUÄź»™ˆˆˆˆˆˆĄÆß&qÓ=ćł4ęU:¾ń[Ę?LŸŒŽÜŪū&†Łń¦obżō”,KÉĮ*FT“öōø3’ˆ7ŁŪ’ųyč8¾> T”\\.öä/ŃšßĒčѣDzīgŸ}’† NZZ5kÖąÓO§RÆ^ĀY-÷„—ŹĒOä•W^!((ˆoģČ-·Ü¢°œąųīďuŸ6ϰÅļߦ›ł ą3iŃ~ZŒP®{}.×½~žŹK‘a¦mfó!;1Õ+s¶)[æŠ ÄT*‡ī ¹„Æu‡ö³ßeĪæõļą(V’°ŠĒŗc¶ņ®¾”å‰.yģ\ažY]l!µi]o-Ś„½bKšWÖä""""""ri:0õv®žzź2‹ŸkĒāć^üé9ī’éŲkĆLcćŒĮō›q’æįwĪfü ŁŒWØE.ø"•Ž‹‹cõźÕ…*[‡£pÕ7 ƒ/æüā¤ļuźŌ‰NŽ ži·Ūéׯ/żśõ=iy»ŻĪ¢E O˜vĆ 7°oß '”™?Ž e|||čßæżū÷ÓQ'§dY'ļnĆöĻÖ½–ł/Óž9żI`Ó}t=ēµ°\NnŽ!«“‰ˆźœqō[Śy[…¹s>ć¦%‘q41¼“QožŲoŲī Ćņ’Ēž}$o܏įćƒwūZÖī‹ĖXDDDDDDDDä"U¤Ą/¼š‚öˆ\ö,ĖĀķvē'k½…hŻūļ-~’ž¶¼nÜn7¦ib†’ĄrVlįå)ēļeå§į‹ IDAT†øH8y+`Æݖ7¶ŗˆ\ތĄ`J8,v¤ķĆC¹óņ…ŌĻŻāš`qhķ¾Ł]‰›G拁# d~Ėa»—Ķį׿‘4휈ėĒ,𳂨›ė®°ˆˆˆˆˆˆˆˆ\¤ŌƦH’—“µp¹\X–¦§­{ Łt~×Ņ–éĀ4MrssėnZä,ųÕ£ŻU!ģ™=©®®8Į„–“Ų™ŗÜ“½oųāē>Ö8PD.M>别Ą”õĖX•^Ąp†äę䞮9įHaG1BĀƉ'$Š>Å '"<ŒbyŁ]Ļīe|æ,“҉WS'¼õ[^IŲ¾ßų!i/^ķ%¹Hiäm‘"Ęėõ’““ƒišX¦§€Ä®ybė^ó$ŻA›'Iē’*nyóZēääąõzq8j,gĒ¤éżŅ6é^ļuėŗv¤i•Ģl_’;ŪŹućį¶Q4»*šŃŽįÅŹn:ÄĮŽæČ8’¬±W jŒ“ɳ'0„F'b­¤—lF›šAźXZä’ćKtb3ā¶Ļę—iÓŁS§Ńų˜9d¦ķ ³D]šĘ•$"ĢAŅśe¬,U‹0åM\©‚ĪCžųūĮ­ÉlĪ&:Ųyź*˜{Yžs‘ ¹µfpŽ]‘įuiŸĢ'Kae„›Hѽ’""""""""rńQX¤ˆ1M·ŪĖeāŹÜ†£X–éĶKłßņę%qN?ī=ӝ÷æ7oŗiŗĮėӃa™X˜ø³2šz=X†C-€åœ±…·dšø0źŒ›Čē_Œą»“ØΠ"*V§iW&vŖÜö"ƒ2ßąŻ±/šŻß PŹŌØG\ØŒ“~°/˼˨žæą)VžĘ½«sĄ"—$#0–¶]‹QférV­žĻśĆ.L»!‘D×öbįGå&-ŲöżBĪLĮtsE£RT)0DÕuIłi5óVGS±Iéü®n ‚滚Püń…-®]IJ“`ā»ÖęXž×NdBSj®ū‚_%×¾ Å“«DDDDDDDDä"c“īÜ;æķ••’ĻŹ#Ōāh²Xf~÷±–eaš^,ÓĢo”hbš^fģeŁüļQ¹lĢ›÷ ķŚµ?§Ė“,‹ÜÜ\öķŪĒčWļ%.l^ ŽsĢRÓżéxļŪDEEįēē‡Ķ¦VN#Ó4q¹\¬Y³†ųųxDDä?–””DµjÕp:§}-­߄āĮaŲlv › ›Ķ–’æĆ0ņž60Œ¼×† Ć ļ5ywĒäŻ"£ž>>Ųlŗ·RDDDDDDDD.<%€EĪBdd)A.(Ó4q¹\ģŻ«°ˆČ…JDD$N§S `)ō+•ˆˆˆˆˆˆˆˆˆˆˆˆˆČ%B `‘K„Ą""""""""""""""—%€EDDDäāg$yĮ,~ų3KŃ‘Ė˜C!‘¢ĒEęĪķd8Ėźü÷āV»7¦°=6 0@‘˜ĪņŌŗ”;Ż[T”Rd ά4v„,ēĒI£ł49ƽ`«”_Óiv;ī’Ž”¬E.bJ‹ˆČ9cemęĒĒ1ł‡„¬ŪžŪB…jõhÓ£w7 W·"r"÷~’—/%iĆVödfćµP2²„3g’߃ ”Īó—Š=U**‘,""""""—£$UīyW®ųŠwßž”;<ÅKQ¶JÅxQ‡±"—%€ED䜰&1²ĻŒŪI‹oē™ZešĻŻĒĘUĖČĢv(‰""'ŹŻĪ‚_ó[F 15ėŃŖt>ž,öķÜJŽĒvNĻ®}›H>T’øØ`ģØ"""""""’7 9šg°xĄX¾]ćŸ71c?{¶®| ‘KŒĄ"EœeY˜¦yōq: ĆĄ0 ģvūŃē"ēG+ƼĢųŌJÜ÷ŽzUõ?śĪÕm:+fīeɄ79cö„Ę&Ņ„ĻĆÜQ?,ļC3ŸF ęƒyÉlŽA®-ˆņ5Æ¢ė½÷ѵV06r˜?°#nčĢ'ļ"&o&žśąvŗ}Y‡w?œzŗ²‰\ÖŦ%?³$y'{3sšŚˆiѕėŖx%%+u)?n©AtT0ž§S‘K…͆Ķ(Fxx1l˜üK³ ’Zwšč5-iTѶ/fÖčw÷§Ƴ2zÜJ×Ń\źƒ7m Ė>}‡·~ŚGvžÜ­_KżŁćŲ~Õm“«ģaõ«=¼ĀÓY‘śŻ{sGÓh*ųģ&uįǼ3~ ė³ÕņXä|ŠĻä"Eœ×ėeēīm¤g¦ĆéŽŗ`Ųm6ĀCJR2ōh"XäœĖYĘē³vrĶÜv\ņ÷D¹¬zļšjŠ®Ļ@‰¶ųkęūŒ|ģ1rŽ’€ū«9$/Mbwt/^ī‡oĪ~›>–a&spō{ōŖāK†up~æœå{{a+ƒ?žHÅ·öTÓUMäāąŁŹŖuØŅœ„ˆSŻel#"”=m«ĒĄĄ·øČ`×ĘMd7¤]‹2ų™9%08tžź """"""rń³ś™Æ~éĢ›ŽąõŹ_ńĶóY˜’ėŸ% ČeĶūńaZI*ty–!Ot&徉üāņąI[ĀĢwĘšē6“€„{čwßćōÜų8£6ū`ŁŹŅāĪ–Ģ7œē?Ģ$s—%ØÜ{ż†2čѕ$ūŌ¢õ#Oņr‡č4z‡vŽČy ŸŹEŠ0˲p»ŻģKßKķź §¼µ,‹ƒ‡°mē¼^įa‘JĖya¦mfó!;1Õ+ć[Šńx`>K„ņy”KEl@żŗå9œr>^Ąm/_M`~ŁĄ+hŚ v Q£XŒŪīaҤ%Ü:؁õ›ąxƒyK2čr}FīV¬µQ«O-üµ+D.ŽėŪ”żģw„E†’k'SŽb% -q¬;f ĄĄ7“<Ńå#Pdžæ:ˆˆˆˆˆˆˆ\üŒļgż{ńąęīōh};ōę‘ŌEüšŁG|47GŪ¹Ł6g ³“X¤³rņL~¾¦ åĒ27y#ĖæŚxl‘s&ńy»įÜk‡ĶĒęO<ˆ78ŅŹŲŽ'¬·6ż™÷/du†°ŒéS–Óå±&0śSķ‘ó@ `‘"ĪėõbY†aąråžöü{öī⊨JlNMĮ4-"#J) ,ēį ¤…§1ӛŗŽõ9“ˆ/w,ac/O½ŗį¼·p-©Ž«©~²Ł•ILå£ekŁāmFՒiS/’²˜ō×”¼‚•ŁU茠¤Ęė¹hĪGžœæO­¹s>ć¦%‘q41¼“QožŲoŲ[«œ’:ˆˆˆˆˆˆˆ%†g)ß cĄL?JTiFĖkoā–džR£Ų½Ü;3ė¤óŲr2HĻń%Ō‘÷ǼU¢* š5¦aµR„…R¶¬·Ć~Āżn— ėøō“§T®p֢Ɛ “:RŹHø=U;Eä<)R ągŸ}–õėןō=næżvŚ“i£½&—•#cg”“ĶuåąļĄbؘšŒ…E©ˆŅJĖ9e /O9/+7lÄEB­€­3^¾a3ĄŹŸß¦iŪFų¼8‡¹ūŪPkéRvG·$1R慈\4pSĀa±#mŹ—/¤¶šxn쇋CkēšĶīJÜŲ<_ %±;3Ļ{DDDDDDDŠ$+‡Ģuß3cŻ"’²Ēńa«0óē‚ }ę-ތ;_½“†æ½ĒS>#y_ ź=7Š®’ö;€'—\osś¾ÄĒūÕ—Č”HżZ^PņĄķvóį‡ņķ·³“×Nē|+ÅPŽž÷9(ŃäZOāĖY˘» •J-šQQł_‘‹‡O9ā*phż2V„{ ųėŠrsrOļö‘#…Å '"<œ@ų',<œˆš0BŠŁ W‘KZ69XžĀż]ģŖ|5ķ_óŃÄÅ,ߒĮ,…™ÓgėÖxźŠ0Žż„'ņß(’¦M›öi;wƲ,ʏĄµ×¶+ōņ,Ėāī»ļaśōéx½^üüü(_¾<Ķ›7ēžūļ#&&꒯Įn·›īŻ{šąƒPµjUń)Ó:ŅRRRp»=’~QõqP©R%öģIcĒöļ %&ŗ2mŚ@HÉ0l6›Z˹aŅōžGi›ōÆ÷ŗu];Ņ“Ję¶Æ’måŗńpŪ¦ÜŽ9Š»Ē=Ė €^“†ä™ļńįĘ+øµ_“£ć’‚Éų |kjE¦ŁņAr:>ŌųXæxnīP–“^fŪĮ+čö\”¾<Š\T|‰NlFÜöŁü2m:{źŌ :"3‡Ģ“d–ØKÓø’D„9HZ挕„jƲü£‰+UŠyČ?8°5™ĶĮD;ĻAJ؃h¹$˜ÅšŃõĪņ”Ä©iģĶ-FHõčŁŽĆŖ-Ō2ģi»Ł܄¶ĶcÓŅ\œu»ŅįŠ\˜{źłl‡dĘģnŒ¾­/wīŔ?‘Z:AÉ,KÉŃĪ9.ŗŽīŽ$ķvŪiu½wļ^®¼ņJ@nn.ż•ĀĒO¤qćĘ|šĮ‡tčŠ^GƒŻćžøĄ•*Ž M5?ńCļpąńxšx<ųśś*ørĪŲĀ[2x\uĘMäó/Fš]ŚaLg«Ó“« ?jŻ;œ·üŽb䄸/Bc¹kŲ#ō¬~ü±hąė“É’ń/ńĮNÅ£čņŅcܟąĀå«ņM·Š`Ź+üVżVŚE)ż+r±1ciŪµe–.gÕźł¬?ģĀ“ūItm/~TnŅ‚mß/dįĢLg0W4*E•ĄATmP—”ŸV3ou4›”Īæ1Ä 8¾ şI4B°ˆˆˆˆˆˆ\,c[w'pM§G¹¾t(%홤o]Ķā÷ś1qAn”–įÜ:™“ūÓļŽįŒ¹k/;VĪęēßėqտΙŖńÓ'£7÷ö~‡‰av¼é›X?}ØĄ"ē‰ŃŗsļüĢ’•’Ļ˲Ą²L¬¼'˜–‰ešłć‘z±LÓ4ó’÷r0c/ĖęwV•éܹ3Pp ąćłųų0yņäĀŲ,‹Žo$ ĄŸ)S¦īv»¹ćŽž,X°€_]B©R„šz½ 6œ±cDzgĻjÖ¬ĮĄ¹źŖc§0Æ×ˈ#łąƒŲµkeŹ”įƒĘP·n]ŖWÆĮƒ>ĄC=ĄęͩԫW3¦Ó¤Iś÷’ļ¾ūŽ;wФI:tčĄäɓXµj5AAAōéӇGyųhĖL·ŪĶ믿Ī'ŸLeēĪŌ©S‡!C^%>>Ó4éß’IfĻžĶŽ;ØU«&ƾ:„+ƬĖå¢zõG×0aĀxnŗé&ʏŸĄ°aCŁŗuaaa 8€[o½UŸŠBš7ļڵ;æ7X–Å”C‡X½n%ź7ÅėõœüĻæ·v8ÄĘž3Yl·;X¼t>5āźxa[›[ųüé~Ģ®<ˆw]‡Õ釩4q¹\¬Y³†ųųųK`ƒRxæĒ|›ų>3śT;õ1įś7ŗõgū=“Ö6DI¹ ’’’ØV­N§›ķōnF©߄āĮaŲlv ›-Æg›-ļµaä=7l`yÆ †AŽkŒüģtŽŁO=zˆˆˆˆˆˆüӚ_gQŖlYBD.j»¶o§Zƒv§5ĻEÓųų¤pēĪq»Żg½Lž{īY5JäĖ/æ¤wļŽ <˜wß}PµjU&OžLēĪ]˜5ė[xõÕW9ņmž~ś)āć棵k+Vü×õY–ÅĀ… ©Y³&ļ¾;Šōōtž|ņ)žžyžž9žžy¾ūn6¤Aƒ+ILL`Ą€|öŁg ņ*eŹ”å7^§[·’±lŁRXøp!Õ«WcĈ·8|8‹įÇӣG–/_†Ó™×żįĆ?Ģ­·ž€ņå˳fĶžxā žzź)Z·nŞ={(W®œ>EEŌ±ńz bccĻįņĪ÷bžIøž­§‘4“9ž§U‘=,żź tźwzć;Źe,‡6rČŹfõ§CłŗXgŽk©äƈˆˆˆˆˆˆˆˆˆČŽĖ=ŃŃє(Q‚õė7‘‘ĮčŃcčׯ½{÷ 11‘5kÖ2bÄH&LOff&ļ¾ūżśõ;ŚŹ÷—ĖUØuĘĘĘŠ¤IRR62|ųpzöģ‰Óé¤AƒL:•E‹“˜˜Hzz:cĒŽeųšatģŲ€aƆQ»v-ZD«V­Ø\¹2Ķ›7 L™Ņ“lي•+Wrå•WAµjՎÖaß¾}X–EÓ¦MØ]»¶> Eܑ.  Ć`żśõx½ŽÓ^Fhh(‘‘‘',Q¶rŖ” V ^9’¼©|óRŽ’ĖAŁŗ0äŖś(,"""""’gļ¾Ć£*Ö8ŽĻīf³é„€@Ø”½ Jé‚ŅD‘jo J³”  T ({»¢ØØ)JWzļ5„ōŗ{Īż#‘ E šūś=öųÉżŅÓÓ9|ųČyĖ,Z“(6›'N\°ŽZµjŃ Aīŗė.:tč@Æ^½ĪÅLėTĘn™2e®jyWĢ^žG?’Gu˜äj°•¤Ļ¬Åō¹ąłV†ŽÓ£·zJDDDDDDDDDDä¼l7{lß¾ųųxŹ”)£ķ’ ŸaŲķv22Ü9®ßétfĶ«lž,ĆĖĖ 3+CÓ²,l6ļæ’Ė–-=łZµj%w߯ö¼e:™qżÓĖ<›··7_|ń9sęĢįĄƒÜvŪķ¼õÖŪŗ"r©³3€7nÜxÉÆ-[¶œ<.9Ų<Ā¢QŻØ‚Ó‰_HQ*ŽÖ‘q«Żął›W«ųPvĄ ÜVk?ģEż’Įø¼¼ ,Xū§ļ&Ē!gĻAęėL­¢Aø\y(Vė>Žųõžœ¶GDDDDDDDDDDDä&vSg§§§3tčPiÓ¦ .— —ĖÅļæ’N͚5p»Ż,[¶ŒØØ(%q:gló›ĶFhh([·n½jmü§¾;vŠ©S§sÖg7°aø\.bccĻ»®Q£F4jԈ1qāDž|ņ‰“óKīqśœ½eĖ–½Ŗåe/…Æ“ ł[I“ņ._×Īµēś÷šĶ{=pVāøgżhzb.…^šĮ‚f1l!.ü–>m’Ā/¶ą®ń‡Ķäņėg¼Č Ö­HY°„Wjø²oOU l """""""""""7ƛ*RrģX4‹/&==;všŃG³Ų²e3“&M¦@ōéӛ‘#GāļļGdd$³gĻfÓ¦M¼żvfvlHHŻŗućĶ7ߥ²,jÖ¬AllŊ%**Š–-[2zōh*VŒ"**ŠŻ»÷\b°ķL!!!<ųąƒŒ3»ŻA:µIJJęĉ:wīœżv8ØP”Ÿ}öÕ«×Ą²,BCC ĖĒ¢E‹©P”<l޼™¼yób³ŁtUäB’dž†Į¦M›² ü;NJ—.my97—·ĒƧōs2ó…J8ėčĘŲfŸws3ś(ŃV^šÜސŚUü€Ŗ9’œ'¾å­÷6SqŠZ>x¼,v ażR$¬ÆŹ[odz·'čŪ#"""""""""""r3¹iĄ!!!,X°€-ZāķķMxx8 6dŹ”ÉDDDœÜnšąĮųųų2jŌhŽ;FTT>żōŖWÆvr›”C_%88˜)S¦0tčPBCC6ģu¢¢¢xꙧ‰‰‰aĈ‘ÄĘʒ'OŖWÆNXXŲe·żõ×_#$$„>šÉšįĆ äī»Ūrļ½÷f»Æa ś*}śō”K—.0dČ`¢¢¢3f »vķĀĖˋʕ+óžūļ>Zr—Ó"ˆŒŒ¼Ŗåeǽyk’ ÓŖI9r’īU·żEæ;£Xٹ7<ڃ»«äĖŃĶƽeė’Ćis{Iģ'ļR„ø½~!^ł~%[Żķ©r‰ķ¹™vņ}ɒ%™?ž9ū­Y³ZWĄuā’2vÆry–Ē‡‘³¼£xbīšĻŸĮ»£FÓ½Ę[Œ:ŸTĒ'ūڰ®v{n" T'ˆˆ\c‡V'ˆˆˆˆˆˆˆˆH®’+Ą:tŠ‘É-7‰’‘”²æĒ²e{1k•ČŁ\¾†?Met“®“ļW“FļLfißź4¾@Ź®ef†}eŖQÉg<‹īÄS»Lf°{‹~?€o„j”v\f{DDDDDDDDDDDDn¹*\¶lY6oޜ£mÆĘ0ø"×£œĢ|6Ć0([¶,v»ż’ė3Āīę‘ŽÆŠnhžńBū²;ē~Ā_nhxžķ=;¾cā“ ĆńKßĒ‚' 8”`XŃ_Ń£f–µž”?ĒŽA€-„|Į{~ūŒ·•¢E©»č÷XY »—>žÆŅ„¼Åß3†0|C9ž~§A—Ų‘›I® :TGD$×üį#„Öćæc¢o?Fæ|/OųPŖf¼ öÓŅo’9ćČ_|9r Ļķ8NšW^ŠWkĶØ)żØœu·±¬Ó†y¶ćž!ĻņæG>ą¹Ūpēˆ:Ō~u.ßųöeŠ›]hvņWjĮ€oGŃæ¦Ļ%µGDDDDDDDDDDDäfäPˆ\_.'ųø\®KŽĻØD÷÷~¦ū{™ļ=›†Q«źTņ‡ŁĄL >ĮĀĒĻpÕČüĶĻ_PčŻLŻu÷é%Öt8æī~j‘½M}L“A—Ł‘›˜Ą"יk?ü¹› _¼Ė2³4…ņbŻĄ7#Ʋ”ų} `%ó'ē˽”4©YūŚžnŒÆģ„DDDDDDDDDDDDnj ‹\G,ĖbÓ¦M˜¦™ć}ģvū•­8v/’’QÆgļŃL’BThГٰØE#Ž §A’ɼr‡ļ5ź„ “gŅźøtŽˆˆˆˆˆˆˆˆˆˆˆČĶM`‘ėˆa”+WīWBĖ‘ i9ņÜUwļLaō5ļ„ ·G䂬dü½†}ž•©UĀļäœÕ"""""""""""7€E®3—2°%K–T§É5‘öŪ`XO‹±³\ĖוּvNļͽ<õÅūÜĖ5žÆŁ½‘i/¼Ą–ūęPS`¹),r¹ös‹ä„IĢŃć¤{óĶ»ŸŠ©ZwJö cE’Äų™I3‹}Ā‚[Ōc"""""""""""’›ŗ@DD®œELōq §ĄžĻ˜“0ėäŗtÖ<%E(b%&ö“9¬Ķh–OĢż­QćÖĘ4ė6„)+¢ÉÜ"•ÅÆ6£śż“ŁfžŖēŲS³ń‹üš ø³ųƒ›ucŪQ„F-*ÕØOߟSsŠ‘ė‹Ą"¹œaŲlW÷RµŁl†Į•«É$6&[D'ś¶õgĮō/Ł‘5R¹u|>“æŠćöž=©”BLLVą•4žžš4ONŪCÄÆšŽŲ—x°ųN&=ū,6¦.ŖŌ­Š÷:öO86‰Õ+6aÆT›Ŗ>)¬y÷iś}“Ā­`Źū/Ń>`#ścIŅłŚč L—Ń|öńl¾ųxĻÕõĪADDDDDDDDDDD®/Z$—³Łl„ēcžoó°,ėŠĖ3 ƒŠą0ģv»:W®¢tbć’q†Qē¾ūØņķfżŃ‰—źz³ķ«Yچ©‹°`l>‹IFü"¦¾‡ŅŻgņRĒbŲ€UĀIŚŃ•éżN×aš«VjŽüžGīŹ‹‘²†Å«=Tzø ™ńÕ1ź>?>ó`eŸ?Ē{&óóŚ~Ō»ÕyN+]!Eˆ(YųäÓOVŚąÆƒ+""""""""""×€Er1Ć0p:”(AĮ…q»ŻW~Ń;ø\.¼¼¼ƒ»Ŗe IDAT”,W™@\<ų‡łaĻ_ƒ›N£ļ¬yōŽŹĻ¬/RóįD:SYé qńX€¹g3[RĆhXõT@{8Õ«äcĀ’Mģń4¢|ž:ÜYŻĘ°EˉkŻ׌Å,K«Ä£·…bķŁŹö”d mMĶ×N6w† ļč$,œdw†{rŅ=+!""""""""""×€Er9»ŻŽŸŸ.—ėŖeŪķö«>¬“Üģ’HH_? |Øuo;Jtż„qcņ²Š«)cī ÅfĒĻ×"1! 0ČĮłl䔎5±’…Å'üĖR«=Bƒ¶Z¼8–eOŅŚš "g7X:t""""""""""rC¹Ŗążū÷«GE®²¶²Yr53‘„Dpłų`¶¢­¹ÆöLżļ å@u€>.HŹ ;‹FRŚū3VÆ>€U43׳Ukį*U–¢vƒ<õ[Ņ`ō‹Ģż~.Į‹=Ōė;ĮA ē'lŻkRøYI¼.z!yćrABBŅ!_{ŽŚ """"""""""rżP  ˆˆ\9+…¤Tšńqe¾7ņŅøū£tlÓ“ĒŚÉś²qąććĄLJ$Å#°>v(ŹÖ©ƒyõ³Å¬\µ˜9ÆfņĪātīRļŌÜ»¾µhŪ$/+&ŽēgCŚŽ€yn§Ė]…Ł5s&’Ąā•«łć÷˜=÷o’ĪNģµ!2ĀÉŽ§3gń*žXō󾎇µĮäČ÷ø³qĘ­KĻ*ŠĆÖ½iŠō ęģ1³–%±tdź·}_c•Y,""""""""""’ -""WĪJ!9\>®“C/{—½›ƒNßČĄĒĒ…u(D ņ.*><š±®±ŒŸžœ€Rué1źi*ļ}Ś~NŖÜݚŅ_L"£ÕŻŌtż³ÜOcLžw˜šæQōż0 üóSŗńc4n…ßéc@A4yā9V½ü>ļ=æ·_8·ö)ĻQÅsŲ8gøhĖ:wi‹«2T»ˆˆˆˆˆˆˆˆˆˆČå2štč“õKµ•õ?+óGm ,ĖĢü!Ū²0-ĖĢ|oš,ÓÄ4ͬ=$ÄFóõœIźQ¹iģܹęĶ[©#ä?eš&ééélÜø‘ŖU«ŖCDD®±Õ«WS®\9œN'6Ū„ ®YµyB±Łģ66›-ė_;†adž·aĆČ|oŲ0 2ßcłÄMęÓ.†ač`ˆˆˆˆˆˆœeć?P P!u„ˆ\×8@¹ZĶ/i -"""""""""""""rƒPXDDDDDDDDDDDDDä”9€E®Ą‘#‡Õ ņŸ2M“ŒŒ bbŽ«3DDž11Ē9zō^^^—<“ˆˆˆˆˆˆˆˆČæA`‘+?u‚ü§ž™8:Z`‘’Bppaał/k`‘ƒ~„¹A(,"""""""""""""rƒPXDDDDDDDDDDDDDä‘ėĄĆ‡æÉ+ÆÅ4ĶÆ÷­£,ž`(£~ŲĻæśé¬ć¬žr2’ƒ„s^DDDDDDDDDDDD䆕ū3€ø÷Ķl^ŗ„æ„ü»YĻ!–}ž9 w&),""""""""""""rsäöxįł+/Ċē·a=yó·ŅL°;żČ“æ(e«Ö§e»ÖŌ)ģŅ™ """""""""""""×½\>üMŅ3Ņ2x6Ūå&,{HŒ‹ĒS¾ #zVĒ™‘HĢžĶ,Ÿ÷1CzĻ£ÅĄ7yŗ^Ø&D‘ėZ®_Ķ! Ąp*DUĄ jm¶hFÕמdÄ[扊|™&!˜ĒY5ē}&żļ¶Ÿ0.Y‹6½ęŽ*!ŲHgÅØ. ÜŠ‚÷?čF„ČXĮČ{_ä`יŒjЁEܼĮtzĒÉó?Š}Śf.ßĶĮcq¤āChDm:<öķŹłŸ’£]“~“}’{•AÓVq(.GP8UZöęŁn5 ż'‚¾Ÿß&æĖōŸ×q ŁEČ’8,œ’Ōa%³żū÷;{1›¤ą•§0u{½Ī ¦nč·oWĻĮ/¬,A…«Ø3DDDDDDDDDDDDn27ĒŠüōhņP[¾č9ƒ¹‹¢¹ćī@6OĄĄ/ ÷@ļb»~šĘ¤ƒH=އŹ:‰¬\ēO›Ło‘ץ³w›ā2ˆŽø•“6”øČ`ėś-Xe»å“ČÜus¢xw?[gź–}<‰÷_žH‘i}©é}vƒŅŲ”Mż`\¾%}v Ōß"zͧŒŸņļGĢ`šmžV+ŽĄ°_üø³ū@ž(źąŲśyĢŚt*līś‚7ĘżAčƒ/0¶Fę‰}$……(ų{#°LvĻ|U:½ƒBīĀępŖ_DDDDDDDDDDDDn7ÉŠf+TŠRž&ī=€;a½—]> ßŻE°U*&yW>łl9‡Ü†_„Ŗ”e2ė6¤ŅŖž‹ćżĶa_Œ ±Ż]— Ę.ÖüD±¦• 6<€O‘JŌ¬Z;•©z˜•Ļgł675+œŁ+aYöõcąW¢·fķS¦T;<̼{ńÜV{ü¾ś)š²ŻGŠļī[2ƒŗżŁ1ļÖgķcʝ Īņ§z„Ź”+åJéJøQ6ÜIG(P¬*ÉńĒŲ¶p…«“Ē/ø˜śFn>V2ž^Ć>’ŹŌ*ᧇ\DDDDDDDDDD䦐«¦½8hƒ‡¼Č’„KO-<ėū•«V1äŗ8hn·ūźTležćŁ·•iaT¬TšTĒŲ Q¹b(©Ū·°ĻFpuj—NcŻŖ-dX‰¬[³›²;S9~-kö™˜‡Ö²öH!jÕ,tŽĪµ(D#žøxėœu9©28ød*/?ńŚŽÅ]ņõ>é™eģßÉw>Ź—»`°ĆQ”5«&ņÅs=xę­9,Ų‹G×Āuɝ–ȉ}kˆŻ·źŌ%cX–[ģFB‚|9ō×WŽü3–eŖĆä_g%ļfž/ѽC źÖ­K­h÷čĖ|°ģ×ü todŚ /šŽŅX:4""""""""""r“ČUĄÅŠe×®]|÷Ż\,Ó¢^½[ĻzÅŹ•|õÕ7X–É-nĮįøņę›ū6³5ÉF”"…°ce$0ņS³vq>üį¶&&ń熂ŌģÕßµ1Õaš8ždgžšiĒŁł÷JāRŹĄ¹rUp×ŗPøPaę~’= ,<¹nåŖU'ƒæ”””t{čĮ+ÆŠ}§|ĖNæZ“¼-Gxi"¼ņ×ŗC§2Õ<X÷w4Ž%KnĻģ²šzõ(~t)óæXĚ¼ÕØ^0”źµJ°cÉ|·x a·ŽF©K‰M[™ĮZ{źOßµ…]VmŗŻIÕ2Å)^Ŗ …OĘĢ2ްfÕ^2.V§įC”źmxü͉Œl—‡Ķ_żĄ·.ˆÜ(#5Žc;³cɬ’įŽmž‡™BbU)٘ą"Õp';ķŲŚ1­Ģ£ožä£³ļ“Ćl[8ž˜½+Õ©ņ/HeĶ‡Ć˜¶§$¼7…QOv¢eƒś4jŚ–žż^£o£¼™`3šåSsėFŌøµ1Ķŗ aʊčS÷<óæŽyŠūīiAŻ[ėR­~3Ś>:œ9Åfm“ŹāWšQżžÉl?µŪ'=@Öo³ņœū˜›ucŪQ„F-*ÕØOߟS³vɦ"""""""""""ש\•ģr¹čŃć!&OžŹžūłń§ŸššēĖ/æ>üķÕ«'—\¾·‡uk×įt'sb’&–ϛ˯{óŠrą410ØK§¶ExfÖk¼ķӍ&Å,vž4•Y»‹Ņž©Śųe•c+|;JĶ`Ҝcī4–āv[ŻzŸ<‰O¬pī}øTĪ:ÖDž@‹Ćk²bajξ~Oxq [_óŻĢŸ mXœ<öh'Źö5źroŪb<3ūe^„+­+†į•²})§¶1.ēŪµ&ÅK„įć>ʚ=‰D€ŅórĖō°uĮx‚ü|±ć¢`‘(^ŽX–…'%†Œ¤h\”„II8Ģ¢ĒŪéĄ²Ąt§ež†ư' Oü\†‹}«?Į'Ø >AÕĮrõ¤®ä«|g?ŗFś\`£4žžš4O~bŠü±Wxŗ„Åö¹’쳤NœÄ£åœ@<ŪV¬ęH‰Ž {¾,Ž©łó‹)Œzb L wo*×®Œó§U¬Š~ˆˆ0X±üõ×¼+u§œćÜÆ¹2]ŽäµVł±aĆæ€wŪ!""""""""""r}rä¶¹\.zčA&MžĀ”C‡ųń§Ÿ²ę4µČš^½zpÉĮ_;žAŲVĻfĄ³³±ył’§@QŹV½—×·¦NaWÖvŽ”{hÆ{æĻ¤9Ćų)‚KÖāž×”sŁÓG¶¤q³JLŻr‚Ū”ÄP°> ŹLf‹ēNš–“ē¬YF~īx KGĒūso„zŸrŁÖo/ՉOgܜ÷ųe¦Ć‡€ąp" śg ÆźMäCošfŠ$¦|óCf$`:ÉWø"õ‹śa'v²č“/˜p ŽtG·”®Ć£ż;aבŪ$Ūžt|\yĮ²°RcČČš» ģŽ`@z!Ź‘7O ‡£W‘xp=ĮEpĒļĆ030 ;ąĘ–‘€ĖEĢŽ5ŠRX®óŲnv'ډ(_ś‚CÉ[ń‹˜žłJwŸÉK‹ajT 'iGW¦ō;]‡5Ā?k[’āÕØ_«v NR]{1kÖrīõ6ükŌ£šć--„ć]ĮiY³ÉFÅĒ*r¾Š³+¤% ŸņāRŚ!""""""""""r½qäĘFłśśŅ»WĻ“™Ą–eJĻĖ žF }JĆA9ŲÖJõ.CØŽå¢ŚźMęµ:}QŚGū3Ź*΃žĄƒUūŻĮė?ŽqņmŽ==»ē%ŌļCD˾ŒkŁ÷"Ÿ!/•Ś?ĒŲöē_ķ,£¦ß§³’:p|ļj\vĄĄ“<vŲ]™ß¬ °…¦‡Ō»H3}ÉQ›#›įøirKTkģN;¦ē†Ķ†aŲ°§Ē½w…¢ZŖƒÆ+šß'¼Å/łz3ø} nŚē(¬Ģ9Ģ‹ĢōėŁ³™-©a4¬z*‹=œźUņ1aÉ&öxQž|»;KS·Z3VnbÆē6"óŽJÓ£¶`'Z·$pŪÖ¦””S­¼9šg8GķŠ1""""""""""rräֆż“ ¾›w^œł+”ĒßS”7«üģßŗ’C;–į ­D°æ ‡į&Ą‘Īń}k(X¶±:śB2V0²C;Ö>»F K&WušßĢO?NēĖNQt.zī<ŗö¢‘”öžŒÕ«`FĶ̾õģcÕŚcøJ•„Ø8ßw·¹ŸuĒ»dįvƒ zmhšßü°’æļ”dĆŪ(vvüךĘ傄„¤3B¾9j‡ˆˆˆˆˆˆˆˆˆˆ\PrbI‰‰ä ”@Į‚xyyżkuY–ÅĮ8qģ&ügæ×[€ĒcrüŲ1ņ…åĒéņεĒĒ”ST$wŠŽ³Š /,lÄÄÄ}b1‡w“½?ė(vÜ$›~ąŒ_žų&ĘķšĮķ93šęķŒ‘įąÄįģóøČŸ/”BĮéŪ±L`¹z ź?ś ĶVæÄČŽ°¹S[ź— Ć×ŒēĄ–uģ/ܙ§šÕēĮEé9u0Æśö¦U Ų6w“wēžžõN›w×äČĀYL oBÅü°ėĒÉLŚV¶OŽzjWUŚ·.ij†±?”8‡åœü_{"#œĢžq:s*“£”uˆyo£iTNŚaräūA<šö>ZšĀ“•œ€‡­3”÷,oś|0–ĪEm@KGvćł%•xeŚ åŃ£"""""""""rćKIL$4, ’““Įųwõõ÷Ēn·süČl6Ūö¹½ģvÜnńqq„ŗĀrķń±éɝŽķ\Ž7©ü½f)?Īūõ+~ćčķÄ¦ŲŁ‘RŒ­É„ٟ^˜d#/Lģx,;–į…e8° ¦eĒX8H³“˜āfĶ–żą̉Ć[1=9kŒy„E£ŗQ?"§搢T¼­#ćV»ĮJ`퇽Ø_2——7+p’ōݧ9=‡łmDźĻ‹ĖŸ| ŗ4-kŻAęėL­¢Aø\y(Vė>Žųõž“õīē‹§šPµŌ-äńńĀĖ'/Åź<ĄŲ屙Ł­ČŲĆܗ;R§T>ü}ƒ)Qæ;﯌;µžbķ#ƒeż"°†įCūO’/ŌAģŸõeółį}Émš°ub;" āņrpKEīņžéó_õmFŅ·äņĀ闟Čƽygi4f.;gmłóŚŌwyįμlżzƒś=ĆSƒßfŚāC¤™é˜øØųšhĘv-ŹÖé/ńȓ/1}gqzŒÅ#åORŹĄŪ+ŽåÓ^ēɧ_bŅĘ|t|}OW;}H „ļ¹—Z)Lj/ߜęEĻóufŃä‰ēh²÷ž‚'†ĪäĒ-DZrÜ8gøč¬¹ŽĻŽä_ŽDDDDDDDDDD$±,§Æ/–eį1M<Ļæś², §Ė…Ųķö’ōåķķ$ĆķĪÕĒGĄ"¹TZrł*7 4¤VC–Ē `sa8¼ĮęÄpxcŲ6/°;1 ;–aĆĄĄ23°Ü©øS)źNÅćN†Œd<éÉv'c–ćNOĘé”MKRXńJ šæ•D«!ļņuķüX{>”ÆŁü±×ƒĒk4=1—B/Ķ`A³‚˜G¶~KÖÓ%)¬xµ%­GgpĻK2“z0i‡¢ÉSĀ Hį[p×xƒĪĆfņFy‹õ3^dPėV¤,XĀ+5\``âũšM؆+i;ߍ@æĻQfć‡4óĶf}@ælAĒ‹ŅōW¼S8†Ÿ‡=ĪÓw÷£Ų†ix±öxQ¹ßWL06‚Š\xLß²ķy婆4ö2÷Ķž—ŠłėöꍏśR(Å_ßāé!]y®ņ6fµĖƒaÅš÷Æ 8PīUf¾[ Wņ.~›0”~M×»h+yåŖóÖ\‰Ž}+ѱļ…6ČGīÆQ§ūÅJ1ČS÷Q&=VŽ‹Ęl *J± ¼īnĀ-xœÉ+¼C&7cČ%·ĆFžĆł©ÅéĖģ”~šC>xś2?źö’ŒßuĖ‘\.°Ótę</Ą²2H=¾½»7³ś›™ĢZ“Dś”ūqēc,ų #6žł‹^rķaüŚł7Z=õsŽŹ²9}q‘Brŗuş”ł[³xhC:N=pÅõ^Š;Žžķ~lĪ£?9øŃÓFŒó%Ģ\ƒ:ö’~?Ė4sõ±QX$׎9Mģ® ĄĀt§c¹S13R13āń¤¦ay2°L–a``Ćā“\EĖĖĀ“<`z2·33°L7–éĮ+ i“¬Åø¹¼=~=„Ÿū“™/TĀ XGw0Ę63ś(ŃV^šÜސŚUü€Ŗ§öżoß@…Ak™Ņ·ģ7ėÄ·¼õŽf*ZĖ—Å4¬_Š„õUyėķļxöćöd†¦ ŹŽN³Ę5pА…vósŻų~uĶź]|}ÓØÆõĮī|w!/¶ÅŖ¼»‡y„^å³ßß”yŻ ·ļŸæ| ”¦B…’Ł —`#øZ:µ¬Ø_`óźĢĢYZxՔ6Q™%Uƒ—õŸVcŚņĶøŪÕĘ+«#Ņ¢Ifłw6­„QćVʎšĒÓ[ć}S]©Üŗ“D+…õŸ¾Ķ’ü:0”q°ęhÉ×_#čžśj¢ n)FÉķé>ų|;€§fģ%ör£yfnļ՜ļūĻc}Ęå>ė¤xŸO•Ś“ö!ć ?ƞ•ĖY¼/įŖÕ+ē²,+gq†«Ģ0ō‹pv4“H.åń˜`d>ÅbŲģ^.l®ģ~ypų‡ąȇ#0æ¼Ų|ü±yū`óņĀpŲĄn`Ł,0,,ò™X6°lFęĖī…;##GOęø7Æ`MRa5)‡ó<ė½źö”ćXĘŽE£^ošŁšcü3šAĘꕬI §žķ%ĻyŚÄ½eė’3ם|VĒQŠŪė"iķJ¶^`ō{±ŠŁŽcf»Ž½u-'%š]Āųø\ø\.|Kõå÷ōŠ'ż"ķ»öbĶa,ŅŲłĶ:ŽIxhBŠ“āŻ­nŅRS/ņWZeš6(@üš•lsßlƾ{ż1:wiGjóņ›ŻˆōŅżBDDDDDDDD$' O ÉÉÉ$%ēš¶U,™=ŽÆm$µm?ŽLæģr½WĶāCO/žišÆgęō3lüx<ć—Äė ’[²¦Ę»ę/õ|Ž(X$—2Ż0l`Z`™Xg¼¬³Ž_Śr 'g÷pŽ sٙۆ óŸeŁŻ$JFRŹžĖ–ķŬUāüĆžD4y”ŃMŗŅ¾_M½3™„}«ÓØT%Ź{æĆā…;p×>sˆeG™jTņĻā…;ńŌ.“™ģŽĘ¢ßą[©„€ēŹśŠQŖ"å¼Ē±n›‡÷W8'ƒŁŗHū0|šõøq˜\žp ٵ!éū•¬7ėńžĖŅ8Ų3RĮŁŌęŁĮŅe‡š)_‘’ ]׋ˆˆˆˆˆˆˆˆˆ\&ė(›·œĄU68ųS¬ūފüŠńĆǰ"½45źĖ /»‰{ź+V„Ÿ’÷Kū¶ÉŒ\ų!ļuÆĀoo¬ēČ9??gSī¤ūyŅē F„ö¹ō”˜Ļł gr‡¶ćÉē¢šž`OżCJHIŹ8gĮ‰Ė¬×²¢a÷Ęü0u4/NŽ#īš?AołņeęČG“Rœ*]śŃ’UƒÄǧ³ Ł~c:¹(ģććĆŌ©SŁøq#½{÷&$$„0jŌ(zöģyr»>}ś°{÷n :ļņėĄ"¹•éĘĀ8•¹kž'»×¼ĢL` <žœl„ŻĶ#_”ŻŠ.<ć?„öe vĪż„æÜŠšģųމ L*T Ē/} 6ž€ąP‚m`„¶å™žĆi:“]­Į6Ĕ¤Ūu¼Xū*–¦jEćę¼Į;µ!ŹŚĆ±|wѱŌBzÖģĮ²ÖŸņēŲ;øĀ6ų–¢”5ž_›I”NČē8Ąīø³æ4=ģ’ö-†•ŗ—Śį°yĪ« [Wœī#Z¤’–WīźÅ7ūćt݈ˆˆˆˆˆˆˆˆČ%2IJHĘćōĄpķfĮųy·7°‚ĘM#āż¹ÆÖg¬Zģ¼@9Iģž3‘ÆG?Ķ“5z2ųĻ3×f_īÕū góä-LQĒažŲ“›C‰vHü‹?®øß2Ų3ūUĘül’9`„żŌņŸ>į—],Öņė;(ņī³tŖ’ ~¼‘Ī Ė4sMkīøć‚‚‚8p )))¼üņĖL™2…ˆˆŅŅŅ2Ļ·;3>’’’r2|öņėęÉ„<¦ #YæēϾXĖĀņxr6³$†Āŗ IDATBėńß1±“æ¾|/Mšvć­åqx6ģ6Č8ņ_ŽģIóŗÕ©Õ¤;Óš1jJ?*;øżĶłf@EvN|”{šÜÉ=Žāė 1˜ųRūÕ¹|Óæ ½Ł…fĶą­ åšķ\^Ŗés•z1†#āėUŁ?õiŚßy­»½Čģ„ūˆ·²iŸBū7Ž„{?x©ż“īń&Ÿ®=LęØĪ—2|õÅŪąØņ3Ži‡żŪgøė֚Ԭ’³ćĖP¹dŽÓF 6p93xOījŁ…a+ ńȜ¹¼ŁĄ’ŌW­„™DDDDDDDDDärŲšó÷Å €»p)ŹY³qĻ©aœķ‰²r{^ ‡‡\“${ā"¦O;Bńī©ė{ęWRī„~†³9wĻåӍÕh?v"ćŸėL»Jø®øß,2ŅÓ³ż­Ų–ŗŠU[})T$’ węüs_H‘"E8|ųšĮŪM›6PØP”“Ė&MšÄ/æüĀ/æüBddd¶ĖÆWŹÉ„23tf÷ž»ģāĮaOf€9‡aL# Żßū™īļeµmÓ0jUJž0®Ś™æyą…wö §é 94t¾æ ŃdŠĒ4t”æ¢xqM /ž¾,Ø ß¤tÉz“ŻśĢś› žM³Į—Ž>g©ū™°ä~&œ±“SwŻ} mĢ® žTīõ!‹z}xŃ?^B› ē×į5ιi›€r/’oŌ…#"""""""""—ĘČGٲ^·ĖnĒn÷Āqśt斛 7x<Łe|Zø—¾ĆŪ Ēń\‡_Y²å“5WTī„}†sVglį×»°ŗĀ4æć6šlĻė^ē©7V±ē‚UW”ƒ=ddx2§e¼Įü'‰IØóŠ”C,X???’’’(^¼8{÷ī=¹ŻŻwßĶöķ§Ī“šššó.æŽ]ÕpįĀ…u£”›ĘĪ’ŽĄ²,<–°] €{Ć>Ÿžņ¤cšn,3'7g7¾x—efi" åÅ»oFŒeCńnŒÆģ„!)X° :ADä;|ų°:ADDDDDD®cFÅ®t)žߎ‰¼pŚĖ[=*—qóŻ_™Ć=›Īņ”+Ķ®Cv¹³Ö!žųšK6Œ|šn駦"̾Ü@l†qşįüR‰]’sÖǬÆįķQmizĖr>8ą}Žz3<ž\~æžģG"K¤²ēĖCÜhy™¹idŹłóēóä“OņĘo0nÜ8|}}š‹Bz2ūŪWØćұ¹^X6'ŽŽŽø\„­Hõö/3į¹üy’=>;–8uÄĢåćEł¹õį>“)@@ŽRŌģѓŽ‰3łd­3Gõ8Ž|ĘøÆņŃ¢]$>Y±ŠģĖõpüŲ ¼ĖÕ”Z°ooĒe†³y«Ń°Q9"B\8¼C(‘ŸōĆM°]°Žæ7yS”MGj‡łąV‘†wD’/GQ5'%īźFĖRAä)I宏ÓĶū3>_~ć…ä,Ó¼ęÆ ‰§Gxyy1cĘ FŽɚ5kčßæ’My­kh‘ÜvĆĢ žf2ĪĢā5/3ć÷¬åfVŲćńdß #„–#Ņr¤ŽĶā|CL‹ˆˆˆˆˆˆˆˆˆ\†ŌJ˜ņ9ąI&īŠnvm_ĮĻÉONK²āŲņA^īŚ‡O¢‡ż{WĶāå!ół+#§AL7±ßcśm#čgĻi¹Ž’0Ź÷ṉ÷`.F‡q›.ļ3œÅņ „h½V<Š=?a¾¤ģ_ĶooOåńŽ Ö»nŹ[Ly²ϼ×{ōFV,\ĖĘō쳓 s/«7¤įĄčķs‚#ėdģĄĻYœ|€sQ0ĄŽ;xųį‡Ļ»nß¾}DEEåxłõĪhŅ”OÖѱ²žgee&f°,Ģ“Į' Óō`™ffö ibšb£Y¹xžīžrÓX“hĶ›·ŗźåš¦Izz:?½{Ķŗ Ę·Ėōd½ÜX–ē“÷ZęĮ²Ü'’Ū<¹,s{/’‚,]²”F=¦“'“ 6›ø^żs¾lÜø‘ŖU«ŖCDD®±Õ«WS®\9œNē%ŸFV­G@žPl6;†Ķ†ĶfĖś×Ža™’mŲĄ02ß6 ƒĢ÷Y£IežOĆ0t0DDDDDDβń(Pؐ:Bä_}š A!!×¼Žų˜üüüžóĻOĮšk35īį(W«ł%ķ£ `‘\Ʋ,222²2v=9ČīĶ>ć÷ģį -O˜¦™õ£²~8‘œÉmĄrŠŅžDrŁĶҲ,ŅÓÓ3oœ¦ū¬ąķYó’^źpŠYCK[f:¦i’––vŚpÓ""""""""""""ŁūĻBæ¹ ™Ķ²r’hl ‹ä2‡ŌŌŌ¬!ÖŻģšgf÷šē†3—µ<ė–ly23ŒSSSńxaYÆ|™/WhÖæ!YÆ`l®¼Ų¼’yaóÄę Ąęåįå‹įšÅpų`sų`8¼ÉHŽÅćqceĖĶĮJęĄ_KX¾3éś YĒłó«łasœ‚\"g]ÓGvīą@|zƍtāķbĻńtõˆˆˆˆˆˆˆČUJrb"¦ĒsMź3-‹äÄD¼’Żģ¶™SxŗIHN¦`į¹śųh`‘\Ę0 ¼¼¼ŲtŌėóI™¼Wū&ģ9įCģšūW®š“ßÓpĄzZŒÅąZ~g­õ°szoīčą©/Žēž[®ńóGīL{į¶Ü7‡š%üø¬³Ž<Ģģ‡Ūó暌óÆwÖę„’įž`]S"–Ź–y³ųqkn »ߥ „—$Ŗr%ņzż;Õzްbī\ŽU}€"!Nt•Šˆˆˆˆˆˆˆ\‚228“gų7c ––…Ē“Čš¤žgŸŁ²,l6eŹ–Į}ߗK`‘ÜvQ:ŠüžA$$$ąv»Æź͆a`·Ū)āėKHH^^^ ĖU`sō8éžĆ|óī'tŖÖR§}ĆXŃ?1~ęFŅĢ"DŸ°ą–ėš#Ś‚¹£ßxJ'X€›mŸ½Ģ[k0`HŠŁ[ Åu-‰dwÆHKIĮ¼„&÷ÜZ‡'¤GŲ¹qßĢŽH…fm¹£¤æ“""""""""¹\rJ yBC)QŖI ¤edüku†v»ż?żĢ^^^Ųl6Ž;Fjjj®>> ‹ä"†aąp8 ¤vķŚź¹ŽXÄD‡°p ģłŒI ŪńFć ¬ N:ė?žĘ’€"I‰%&Ö²¾ØĶh–OĆų/—²5Ę ¤T]:>öŻj„b#•Åƶå™-˜3³„l™õūā Z¾Ģ’{•FĪĆ,ž2Žę­bŪQĮ‘·ÓķŁgčy¾’›ucŪQe,€“;†żĢŪM\Ł“įtNĀJW!,«,ūB'ĘĪüDV­Jy;`ē÷_ąĆł³ķ`,é^”4|n2o¶ fߗyjāˆKĒ‘§(5Ś>ĪąŽu ³V2[¾Ćšiæ²įp*^yùż±Q¼Žź–¬ś-Žüš2m¾>Āįd/BĖŌćgśr_T€‚drż~ß¹ņRØ`A¼Ā‹S¦B9Šžš?Ī_Hį-‰ōĢvž¹˜å›÷h᛿$5n«O„üŽē/4ŪķM.šĘØE™—jч»J;.½ 11‘ÄÄÄkRWĀ5ŖēF”°ˆˆ\&±1qŲ"zŃ·Ų'Č­¾:óäa $²NEVĻśƒæ·'P¶’‹ƒK¾į»­Ōø­żSٳbæ}÷;4¦ų9ĮfpąbŪŪl„UkE³rx8²ßĻ©C#""""""""×€EDä*H'6.G`uī»*ßNa֝x©®7Ū¾ś˜„”m˜Śø fĄę±˜`Ä/bśē{(Ż}&/u,† ØQ%œ¤]™žŃļtÖæjõØęĮļÄŃ鮼)kX¼ŚC„‡k°_£īóčÓ8PöłC,¹g2?ÆķG½[ĻÜøBŠQ²šÉąŖ•ƒ6ų_R?ų—¬Įm5Źqś`$žµi˜õßå˲}~W¾ż{žF 6†X+€:U«Q±ŒPęœ2ƒ"o£Y½Ģ2«†ģgI÷X¼ŁĶ­Uõ5.7#(Œ0o‹Ż1qX©Yõw"Eµ£N)Ā&°kŚl=Ų€āEĪŚ9uēÅ·ĻśĆ×//”!A§²ē³ŪƘ]FDDDDDDDD®;śåXDD®œ™@\<ų‡łaĻ_ƒ›N£ļ¬yōŽŹĻ¬/RóįD:SYé qńX€¹g3[RĆhXõT@{8Õ«äcĀ’Mģń4¢|ž:ÜYŻĘ°EˉkŻ׌Å,K«Ä£·…bķŁŹö”d mMĶ×N6w† ļč$,œŁ‘ģÉI®8ž“Īž…S;ćWÖķ‰&ÕˆW’GTzęqÅ{xØĘBŽ~ü>64mKĒöwŃøL^.T­­`a qÄʛ:ļäʓ5å½yāĒÜéÄ’<™1?Ÿv«1Į+)żÜ[Š%nŸóż|tLDDDDDDDDäŗ“kĄÆ½öėÖ­;cY±bÅ1āM ćźĻz˜‘‘A½zõiŌØƇ»č¶–eńēŸHdd¤Ī"’HH_? |Øuo;Jtż„qcņ²Š«)cī ÅfĒĻ×"1! 0ž‰ō\Œ‘‡zwÖÄ>üŸh@š/KH­ö B 8ŲBhńāXz”==\jĆ74(‡óćZ’zϘ;fÓŠ§ŲŚ>ĆŠ~‘„°—Ļ_Āo’lą,Iē1ŸqėŸsłdÖ^ź:‹ŁŒeb·HĪ7©a³cĒÄ£ųÆÜ`¬Ų#I7Č”y0ü(Ū¤-µĀNŻĄĖĻğ{-_tū‹Ü.k?‘ÜĖ–[¶yó–s–ķ޽‹“““K.˲,zōčIžķśÉŗ mŁmo8šr@Zź’Ł»ļ€Ŗź’ćĻs.\–ģéDCÜYZ™šŚR+[–Ms”šåȕ9Ņғԓ4+µ_„iŁWS33w*¹PĮˆ².Üs~°•iŠ÷‘;Īųš¹ēr?÷¼īēóÉ,ņ7@-ļ~„B!„B!„ØB*MąāzügąĄł—+Ņ#8>>ž-Zšžū1™Lœ8ɬY³Ųøq#Ū¶mÅŃёåĖæ’#BTHllŒT‚ø­4M#++‹Ė—/ŻŽ‚čé¤e€Mn9ř.Ļ į€c2{ÖÉż“‘66hÉ©¤ė`劎§śyóܒw˜`ūśĄńuŸ²čd=ś /˜{×6Œ^]yaĮ\,œīgv[ūœŽ½NšŠR†|9†Q†Aō öĘKdR-zŽ„]į—CūYńĖR¾ģC}ż Īķ¹/؜eųŒu}©£˚Eėń輋‹!Žó©…óšŻæśŲdűėd 88įØŹń-ī\zśeĪž=‹ĮœÅ•ÄNFāče»u ‘ųŅ,`7«÷üĢĻjKkŲcČJ!>ƑĄĘÕ1ę~ $łĢqN':įćTĘņŖ3nģ=ŗ›ż^Mp#™+6>4Ŗ^Ęzåų]._¾D\\,–––ØŖ’üsbbbØQ£ŸžM›6`ōč1Œ=€„Kæ GņꫯšŚkÆå–1ŠęĶ›³fĶjĀĆƙä‰'ž ++‹éÓ§³rå7\øp¦M›Jhh(ŗ®óÅK™9sgΜÅĶĶńćĒŃæ9‚o3OO/©q[iš†Éd">žöĄWŅĮŚĘ:če«F½5¦šB 66ÖčRHÕĮI±¦É‹³ųČś#ę.}—Ąµ~žłƒ ~l¤iļ4Xż9Yö¦ežŸ|;Z¼>‡ŁNóéO3öYTó¤A——éŅżŖXq¤ė«#Ų3n>󮶝l»Ś“Ą½AõŹY†ėgh8I#㙺tÆÆLÅli‹£kkåŁY—Ošæ/W2ól& {j5nĒšwūÓŠČ0ĻāŽ£becƒrf߯ޅb0bcļBõ:ĶčŁ-gĖüē}ķv½če³?oįĒæL`e»_[ź7£ā@ć°¦Dn:ÄÖC>Ō Æ^ĘņÖ4ļÄŁ_·³}]$šŃ‰z­½hXŻ„ŒõŹž\\\ńššÄh4J,„B!„B!*„kæĮ¹#įé¹’tŠutt]CϹ€¦kčZĪuM3£kš¦åž4“’Ļīm®»  $33GG'fĢųGGĒ"ˤ§§3räHbb z]~łå—eĄŗ®Ó«WolmmųśėÆóo?|ų0įįķ˜?żśõ»&ž4isē~ĢčŃ£ %&&†vķŚįģģL@@ <ņżū?@ķŚµ±²²*5nŪ¶-ķŚµ§fͼņŹ+dggćēWoļ:Œó«V­bŚ“©ŌØQ“?œĪ’°{÷.¢££iß¾£F¢k×{ˆ‹‹£V­Z2’šm¶uėļtļž T„ø­ņąˆˆBCC„B„āŪ»w/žžž×7 ĒŽÉ U5 Ø*ŖŖęž4 (JĪeEEɹ®Ø( 9×QČłÄMNJ]žq„B!„ān±s=^5kJE!Ŗ“˜sēšė^”u,*ć/2`@9xš óēĻ';Ū̳Ļ>CXXĻ<ó “'O¾®ķjšFFF&“‰£G1nÜ8¬­­iŻŗĶ5Ė&%%1ž§Œ92?ĢĶc2™šōōĄßß’šŪĖāļļO‡ņÆ'$$°xńbf͚IÆ^½˜9s&ĮĮ!üłēŸŲŲŲ ė:ķŚ…,GŗB!„B!„B!„¢X•rœŗ¼sŃ¢E\¼x‘„„Ė|śét]'((čŗ‡×[·īgÜÜÜ©Q£&;w&>>žåĖæĀŪ»Ī5Ė=zŒ+W®~ÓßćĒOpåŹ† yWW7\]ŻĮd2KXX;v䔇ā•W^)×\ÉB!„B!„B!„Bˆ»ÅŻōˆ‡‡3qāD¬¬ŒTÆ^ww÷—Õu½Ōm7Ģž¢( ²²²+T.]×QU•łóēåĻ/œĒĖĖ +++VÆ^ÅęĶ›™;÷cŚ·ļĄŲ±c>|˜ĮB!„B!„B!„Bˆ|•²šĮƒxī¹ēpwwĒŁŁ…—^z EQųēŸŠ4ķŗ¶ėģģD‹ĶiҤI©į/€ŸŸ/F£‘?žųćšūEĮŚŚšÄÄÄ¢•©Ŗø¹¹qģŲ± •+o_‘‘‘4hŠ Č·ƒƒCž>;wīĢ÷߯įå—_fĮ‚årZ!„B!„B!„Bqwؔ=€W®\IHHĢ›7/’ö+W®°dɒ[RWWWž~śi¦M›Š®ė“lŁ‚ÄÄ$źÖõ&00ĄĄ@¾ūī;š7o®ėø¹¹֒x€Y³fѤIAAAœ>Uf`ķźźŹSO=ÅģŁa0XŠŗu+ŅŅ®p™ĒœÓ§O³uė6ČŹŹāȑ#8;;_÷PŲB!„B!„B!„Bˆ;S„ €/^¼Č°aĆyņɁ„†6CU<ÄŅ„_wĖŹ1qā\\\X¼x1'NÄĶĶÉ“'Äĉ^!n¶­[§{÷oś~ržsZžw…žąŠ’?7uŽeqgŃ4 “ÉDDD”””R!Bq‹ķŻ»ŒFc…Ggi޽“Ŗj@QUTUĶż™ūŗ­ŖØŠ ¹ÆįŠ¢¢(ä\Gr’“×x!„B!„(FÄĪõxÕ¬)!„ØŅbĪĆ?¬{…Ö©4]HG- Å0›Ķ\ˆ=KBR WleEĮ Ŗø»xįāģš !„B!„B!„ø»8<ŗ”o{žÉĒ/~ĀŚäkć”l§‡>ĖūšÜŚņuFŗgĘOōł„;C~µ(öģu÷—3čšHYr€ūēüH×ļŗ0t›]…ĖÆkÓ¤ētjˆÆg5ŒW.¹‡’-_Č·ĒĖ÷”lÕh‹5é\1鄎vu¹…Øjd a!*1]×ÉŹŹāRB<ĮĶ*ŽźŗNJj2g/Dc6gćīę)!°B!„B!„BÜ„4ū®Åދ𠽱O6å €Ōü 33ž£ļg±d•xŪ ,··‰ĄBTrf³]×Q“)³ĀėĒÅĒPĻŪ—ÓQ‘hšŽ§‡—„ĄB!„B!„BqR“Ņ0Žß‹ök—²9ݐ»Ł®t½BģE‡ŗæˆ•s‰øŪ1Ūv¤GĒDvŒ[ĢĻ697&^&īL`yĆėéF•[ˆŪE`!*¹¼9€įśę÷Ė4e`ceK½:~œŒ:ŽŽŽ—Gu …B!„B!„ā.cü÷[–9=Ė]¾aĖŚ,rĪ<«TėŅ—īĒVš¹ż[ō.“¼nš aŸ—Ü„!~ÉÄŽČן­bSl޹e¶-^åŻ^ᄸfxl«,gķŁœ{[O\Ļó»ä™ĶŖG3Ö„Å€Į<ŻĪ‡:–±Dm’ŠO¾ų‹£éWõčUUTÅww;T4J ŗ“ķÕxn9sīÕ±`?>˜MĀ׃^mń5·=±2åšrwż`į{·‘Ҽ -½­QĪļ`żĀł,9œ…č5 z|/tōĆŪ:žč;8Õ0€+S†ńéYģš āåžį4Æc‹eĀi¢¶-dü—'HŠå˜7‡*U Då§kyÆŹu|ē„ČÖÖ6ųx×')é2qcņCe!„B!„B!„wE;Éok"pčуęƜs̱½ļ7±ķū\Ö wŖFŻgfóa«6Ly‰§†-ā;½oėE3cŽłewē8¶OĀÓÆĻeiRw†L@G[s9 ćHƒĮÓxĖż{ Č#C—ńk­‘LXėšE ©›łńw# _Ćōī„“Æ Ę nļüēżyķW…ŒóPϾ<±2„Ųۊ£¼ Ja’§Cyžł1L8Bßįżhom¬©9p:SC²nŅ <õę<¾6w¦}͜„³ŻśšŚˆ l׎åõ§1čżÆųzG IžŠ›H`!ŖMĻė 'OFrōčŃ2æOžŒDQ .ī"æmŽĄĘMė9|ä µjzs!īQ4R5ŪßCß.1ü>’+6žN"ńü.ÖĻł‚’³Œ'²s—Ź"ź×oųß©$’bö³éćOY©?Ģ£ķ e–$ŪµżŪ`Ż¢ķJ4‘yq7«æŽCZ«šbŠ}™£Ÿ¾Ę«KN’ś<#fÅw敎ī8(×±½ Ėāü–ÕüļD"I‰'Łæb›ķ[ЬvfūūxģŽ‹lY°Œ_N&‘{˜m_¬åģœĪģ\ o‹¢ž=Ķ…Ō4£žaēńT2õš IDAT9Ē&n*ZˆŖš’\Ø°ÆÆ_…Öm×ŗcŃ'½…ŁŁŁdggcee%•+*ŁĮžĄ”­[Ų×y`łBż ēīćLµĀ|ģAĶoÄcPBVō±•ōńM#śą~ŽŌõ#¬¾ćµĻķ¬ų‰ćuīg`ūZ9 ES~ßĀߑ±¤fØÖč^žģZ›Œ ēH4ÖÄŪÕx[ŽÓŲ“‘œ«ß ŹńÜ7‘t;Ė+„B!„BT"Jöæüü}4ƒ¾’ķgńėiĻžEqAÆAŻBĖe×Ŗæ~ļ¢ ęŲ5¤žĶīoŠ”¶+pmoY5c{ŽŁŅ·Ž'[j9²½źPĻŲ„ĄiK¹'ļ-æE5Ü Q%”;ŽČµ3·Īdžķér’Ć<öę ķ^äÅuW*¼½’BĶH$!Ć W ģš~4RņĆÉB‘[”hĘÓėų6bĆ>Z@č®müžė:ÖH&CEqI,DPøšńć'ČĪĪ.ūÉmaAżśÅ‡Åšf–Žæ7ŠĻŸ~Č’Ü_ą¾>īŚzHā×wū3vc<™š‚Įh‡sõzµčHļǦ}››³ßģ¾xūmŽ>ń5-Æ7.RvP-mpņ¬G`ėn<łtš»ßeźØSq[eĘb۟ū9q!‘t³kWj6 £K˜7ve¾4RĶŁ{«ÜĒ^ćĀßŲ|օ¶÷ö¦–-d[:c4DzmŻ:.†¤Ž«±ĒIG7,ē—cidė ŒŲ:øāUŪ—  |œ-oN„˜cŁu]åB!„B!īD:ŁŪWšõ£o2ąå“ųf,ćķƒ×ĘEŗĮ€Į`‰Eį7Ņz6YŁ`6—ŌÕLV–¹\S*Ł™dš÷ņۈI|u¹ēō ’Žüʚ#²7} ‹ī ƒu›Æ{×Y‡戊ŖZ`”Ī]É:ʦw°7šŗßӞūF÷eąI¼>uQšœ„7‡ĄBT/– õė׿Ū%æ~§pjĒV"ŪŃ%Ą”ä°@»Ą¦…ńu·>ŒéėsW˜™äÄ$ĢMžaž0ŒY©ÄGfŪO_2tĄZzM˜Ć˜Žī•tށܲ=ĆüW[c“•Ę„3ūłqŁĒ¼øć4s–Ž ½4ÄD‘~œ?l&ʾ1aŪąj­“žCŒb…•Rō½IńļH\ y !ł “9w.ūś]ię[½ą9l¾TfQL—Nq<ՙFŽN…>£‘™žŽV½%·­ƒ…9““„XNFģį‡vėÅ=¾Õ$ B!„B!n2%k??üĄćCjrlŹļœŅ × ±eq!šH5œ†Ł¬ż'gD-Ķ€ŻxNżr °ĪŪZž:ŗECūdµęeEP–gŽ‘ż8­B­Yń›ł:†DN'11=Ū\”ķ©ŠR®ŪŹĖE$ i¤ńĆž’:“dxh-_ZĖņļ_bĘĢ^ÜWż/ž“‘ŹÄĶ!°U@ŽЊ¢pōčQĢfs…·įźźŠ§§g‘ķ‰Rdķbzæ>ģó0K €Eц’£7Į!ĮŲ“hĖ}=$lĢ ¼7éCB§ņ€›Ł1l[<‡…öp<ΌKć<żęPś5.!ō)słl|Ō‡¦¹gņFftµ®š~T'o‚›唽Y+ŚūfŃēł5üß߯Ҧ‹MéŪÓ.ńĒgÓłģ·ƒ?ŸˆÉŅN#1­»Ę¦Yłģœ‰K";j5ėA’ŽFö¬ŻČĪćq˜ŖyÓöńŒŒ£ µf4Æ/Ųɹ$NŽ“čõ ļ¼ŠŠāųmÖD>ŪÉŁŲDұţa8‡㉠ūœß­Ō²–cżāź“sŃė¶ń½z¬+æ|?5§Ü'>ŠĒaž÷Ćh.-ŒŪĀ–s™Õš 3Ķkęʵu}ń/śŖBŹ‘_Xr(•”,;÷z4kß‘ŠźV Ē³cÅ7©Ū—§Śz¢’MV¶Nāžo™µĄ€Wxžhšó˜Ÿßś3·ę4)ėß?˜‡XäļćJŌ.žˆ·W X;S³F ,j×£a ?Žėæć—ß¶PĖėŪZ '’ŽĘ_GĪŸŖcėéK‹öķö,aś‚2—/”¼ŻB!„BqGŠČŲ4•·b $P‹żĢøÅåu¬Üڇ9/¦ē¬UlŠ÷¢įcĻńHź—ŒŻŸ\ńyči8õ#[.ŗįóš+9‚gāēńõ?©dŗāpœŻ‘EHÖģŚóč3µaļ^ž‰ŗH|¦.=ō`6ķ@-s{f.]LĄŖUkš¹üľ4ČĢ,ī¶ģ Õ¤EĀVm}”¹/¾Čó¾cc¬+u{t”…į2›³C3īižĪ™'9j‡›Ÿ'®¦¶§ØrŠ›FNĻ Q^Šõ‚Ļ*5lŲš†nOˆ›ū*Sƒ^ģˊ'±fSŻqąĄ'o0üן zóFy$óגé|0|5¾MøõÕHg_iĖs^Ź˜Ęūz¢¢RĶĖŖģõģŹ.ŗĮĘk%›L“¹ģķŁ$rxėœ­ż<“Fć`NĮąķ‚Ź)"÷īēRż—™>¶!–ɬœł S?nÄĆ/¾ĀŌ—lø“}SēgaŠ7Œhj (ø6éÅėŸĄĆ^'n×r>ųt<3¬bJg{Rr¶éū"SF5˜~†­Ėę1ćm#uæM[Ū²ŹZĘś–%ÕiaV„“ ĮųėöÄĀĻC=‘ž‰Ā*ųü„uqŪØN8*iD9M²—%|čŌŅ„>mBjQMIįäī?Ųņóø ģBŻFGr ģNϦ®(€„­ ©€ŠG³éęo‚‚•½Å)8[7aļņ<‘B£`kĪo’µĒģiŃžAŗTĖ j×ļl^ū»Pļš]eq®“å ”PŽ2֓į !„B!„ø“Ļ#˜Īq|PR÷=‰£ G2īÉĮ<õĪē½ŠmWŠ7wž¼—‡Ÿ’‚oż¦ŅėĆˆžāu^NĢ‹ƒ?įK7ę„S]=ćšXWā8ی{ū å”ź®8’H8sˆŸŽäĖ?2s—ŗRĘöĢ\Z’) 3bĮĆhŪ&ÓoĪæÅŽV1Éżģm&>óOŽķĘÓę3ž3š3š: Ū¹įž ŸńÄĆ6‹ō³{ŁĻoӆ1fĮĻˆU𠾟§Ģ`Dēź9ƅjgY=t“~>Äɳń¤Qš!2tÖ\^kå„RÖżYQ¬›4‚÷—oęą93Ķz1bÖ,^lī˜sIåkÅŽį~†XÓgå%V=j[\qvł@}~†3©FŖWØ fŽ-x„žļmäŌ„ ,ŻŃé¹iĢםš@;Ē÷#žeņO8OŗĮßV=yyād†“q« ÉZ ihoęĻÓg1'_dŁ÷ióÖ§ ī’S?ŽŗĄö‡±q’pĀ[]uģ'o)}ł–9ĖY»ÖĮĻ·Vžš“e®×¶˜TGĖĘd2”šR‰;½—ē­āøUżBlѓ-}{­sŹÕ|[о…Įp·¹Ÿµ°ónB«P ćufŪ–Õ£sļN“¶‚aĻśģ;p­i]TŖłµ¢Sī&:pā·'łń`ę΁ł/Üvu›Ņ¶…?šŃĀż;žŻĄ¶#Ł“ńŪR޲–¼~Ū āė”"vŖµ§™Å‡lż+‘GrAɌ`ßæ*M^n‚üپmĒ&ÜŪå?oYĒ’“īų4ö§IPc¼-‹<~Ö>4¬ē‰ Ō“MāŌ7’r2N£nĶb·ŠĮĘWW×kŽ ;gÜ\oČ( Š£V:§/'”gœgĻĮT¼;÷”użœ#Ź£S §¾Ųɱó©WēŖ•3N–¾|ķŹ[Özu rP !„B!„øc$óTž9§béŁ1ö~vŗI͈dēĀ‘ģ\XxĮ‚pwćˆēŁx͆ īß1¶{‘ķ„lːM…Īh9¹ę}F®)½ģ†Ō#üõķžś¶Œó elϐō'«ĘžÉŖ2n»ŗÜæ ėĮo…"5%{ŸŲ¹gĀԌćlŸ÷&Ūēåܟķö$“»V'ķŠ‚Åå_ųbĀ/|!‡ ø…$¢ (UĒ:%ĒZ¶ś2žõNŌP¢Y7mdŹ āŁę¦~5ŒšN:ē6}ČcŸdDČq–÷qBŃ/spÓļœóŸĄ—Ÿ4ĆśŹ)6:‘į÷ qėVF[Vƒ8÷3ęØcœHæĀ¹‰=hł~žIv–ŠU|Ś5CĶTtłņÆg¼&°ŹÜ:m'ä¶ą,qņ ē„iĆéS]Å|šśŹA1_77”+‰$d–€ĮWŲ“’·g·,į£e›8O†ŃĖ43A¦’·Z£65•$“µėŖ³Āė——āܖūZĢdņļ;HčńĒ÷±?½!†9ː鷕—Ę÷0 ~ē#pčŠ~Ų÷7ÕĆī§GĖX÷ų;8ā džłß§Š.lcÉw{IĢ?”.0oöF@ŽÕć he[®æZĀE.f›HŽøˆŁ æ~eڵυŠ._žõäć B!„B!„øu}hpy??J/_q›Č‘'D ė=€’ż÷ß2{F4hPęöŹ%i3ę¢Įˆæłņķ`Œ€ÉluZ|ńŗ3];t¢US; “`ŻÄŸųpīaĒģgń°FEžąč ?ņį¼#4³Ÿ…Æ4ĀtjWŸ”C”|8c-o®ģ‹cĪo}£tėŅ :ѱęi6¶łŠŸ÷fŃ-¼ōūļ ś?f.<ǽŸlįŻ¾n(@ÓO¢ŲPßżń1ŻŪ”\>róÆśRśl *.Ķzņč-°ŚyE²”õ—å+ĆżV8ŻGĻÜ^—Ķ›:sčŪf|ńײū“Ź™‡ĘøækĪöļ½/„E[>š¹W—ö ²ĻR©EGšoŖŚus{“Ŗ®Ü’īG<ŪČP¤mŻQˆ)¦zK[¾“‡„bėY†¾ĄüW[ak“ÅŃĶ‹.6ł»Vęö.—’…×Ņ0aÖu@ÅK Š5 Š"W0rĢ·Ø½†2qxc\‰fÕų±l.e›ŠĮ€ ³VžßżrŁė—‡āD»n­±œų[.ßG“]»ˆõéBO™»¤r“šģ©Ń°5†Št׬ük3{ė=N±(**:ś ˜^u„÷ćČF'õßßXėKļŽ>X”`aėŒJfÉÆM‰±Äšœ\QŠA±£Q×^„y>¦,ķ¬ä«×.cł÷zė !„B!„BČŖÕžŽ5O±ėPÉĪįōА+ė?d¶t•·‡ĄBT…{ģ6nÜų†nÆ,ŁGv±/­võ§ø©-Ū fd—ļ~o»—†ĆS#l0\:ĪĮuĆöS*ŁR5āv“RBT~śč–uŪÓĶŁ˜±ĄĀ¢„Ź*ˆWׄūoĖųdę,žiń!s&ną×QͱеR†ĘÕ¹žßJ±°Ä¢”žŠEī×uP«óÄ⠌jVųĻJµź.(ZéeøĪg)‹r—A;4GŸ‹į¹Ł|ńQs<•c,xśq~(kŖ 7ųøø“„SģŁ³«¬4.9̶Ÿž §é=a8ŻŻT:0ą”„ łr £ ƒčģ‰1#–ȤZō¼?;Åg »’ǟgj^»Œå uhģgdÅ/Kł:°õõ $8·ē¾ ²öSĮĒŌ©ŒķŻĄ:4Öõ„Žž-k­Ē£«/.†8ΧŽĀ²–T§W?6v(Ö”ōķQ“Ė's6„õFņßŪĖ|a/æFdQ£–޶q™“ūqŁĀ‹¦ī7šŃQńp³`ļŃŻģ÷j‚É\±ń”QõņõšÕÓ/söģY ę,®$Ęp2āG/ŪŲ­ģĄ—f»Y½ēg~V[XĆCV ńŽ6®ŽQ±ĮĘ’Ļēt¢>Ne,_byĖXO)!„B!„B”ƒräK¦½ł„T„Ø4$B”žGĀ·1õ óŲ±#-̧ųpG©†_×!Ģźś$}‡·¤óĒ‹ųsXs:×&Ąźc¶m‰$»UŃ!–-6#Ųf.Ū¶œÄÜŖaNÆ«ģćlżć¶ĮĶh`A~oŠė.{ż&ų[ĶįĄq3>żÆ9‘Æ—R>lm )! ®;Ō*« i?ļęĪüqOŃÅÉś.eģĶɟ;.`Šߜф+öNŽØĮ«/}ji‡sõzµČģ÷¦}žüÉv“x}³>ęӟf2ģ³4ØęIƒ./Ó„{vjuxö ¶Lżž™ß·§õkAe,ļH×WG°gÜ|ę½õ;Łvµi;8€{ƒź•¾^…Ćż2Ź}#k²į@&ŒgźŅY¼¾2³„-Ž®u¬e_ĪĻ$üDz*%ÕiŻk ,hššc„}=…æśÓŻ[āßŪĶlaõ•ģŁ²—¤ōl£-Īž>tģ՚& Ü°ĻXÓ ¼gŻĪöu‘hF'źµö¢auėÜćTĮ)ō^ ½z=+”3»ų~õ.ƒ{Ŗ×iFĻnAų8ēõY7R»]/zŁlēĻĆ[ųń/XŁćīזśĄØ8Š8¬)‘›±õuĆ«—±|Iåu)c=9¦„B!„B!DÕ£tķ78÷T žūO=g8]×rz ź:š®åĢOØėhš]ÓŠ4-÷§™”ÄxvoŪ 5*ī[·žN÷īŽŌ}čŗNjj*ķśƒ®»”kąkžčŠB£F0r·Üøi=­Z„S­Z5„Œ³Ūś%~BŸŸj3dŚXś6R8¹n£¦ļ¤ÓŖ‹, ŚČ‚ß5›ŌĘĪt† “3įĢsüµo"M-RŲņf÷-TxųwŲŖ–‰§I«×žĮš×˜6tžXį‰É spŁXĘ|cąĶŪ™ŠŅ̙м%+ŗm劔œłoIśŠž^/`żUßō:Vśż}2ŁüFŻ?Óé1j ƒŚÖĮ*ķ ‡/ūņōĄÖ8(„”ÆI:+śśņōž{ł`öKéQ\tˆGźoṖϲ£Ē·üżŃ=Ų—UĘ2Ź`»’=B[ĶÅžåŁŒ{4w‹s,{¶«Śm rnG,͙м˜z3rŲc“Ŗ G¾žĄ;ĖÓyꗯĢloKę…U<ö>ĻkӆņМ$ĢĘjøx5¤„Æs” MĮŚx‰ß¦<ĒäØ œvℯ?bBĒj;Ÿ³^ ‡ƒw² Ī;IŖžĪ”ogš“]?>ķ"įÆB!„B!„BTŅXˆėT•zēńóóĆŚŚŗb=€‹ažw2a”Kč“9‚é­,å`ø™Šėa\ˆ¦i˜L&""" •ś·čø<ŹĀg^dĮ j6ķĮŠ1/Ó©ŗAźEܕ¤°B!„BT^ŅXq'ĄBÜny`²9¼śvh š«éŒ!ń0?|š‡ė=ĶÜ …ø+ņĀŅͼ 5!„B!„BˆJ.59Y*AqבXˆ*D×užż÷_“ÜaĖĆ`0ü·ŠXOāō_k˜¹ņŃq)hÕjŲń9V|>–ÖÖņ˜!„B!„B!*/gWW©!D•–š’Rįu$¢ Q’[¼SW˜¾…¦Kżß† ŽŻ—Ī»RB!„B!„B!„( €…Øb*2° ¾¾¾RiB!„B!„B!„w‰J’üóz¾żö[ŅŅRK\ĘŹŹšŽ½{ѧOyÅ]ēÖĻ,ŖŠóēĻK%!„B!„B!„w¹J/_¾“)“ļ¾ū®ÄeśõėĒźÕk*m¬ė:‹-ĀĶĶ^½zÉQ&„ø%jŌØ!• „·XLLŒT‚øcéŗ.• „BqQE*A!ī•&īׯ_©×Ɩ•eŹ_¦“°ųvŠ4Å‹—еė=‹ŅšRUõ†nSUUiŠ !„BÜå$ąB!„iŹłD!„Ø:īų9€5M£{÷ūپ}{±÷[YYqäČæø»»ĖŃ *%UUquqē·ĶnČI:EQpsńĄ`0Hå !„BÜež[{Rc!„Bˆ;KÅŻĀmI ƒ…¢r«4šĶźÅ«Ŗ*Ó§@RR ,dļŽ½Ģ›÷ ƒUUqrr’#ATĪ&˜¢`4ń©ėG ÆZdgg’÷'½…ÖÖÖXZZJCM!„ā.P¾ŠWÆŠĶB!„āŽh)\,ń4”RfSĪ1ž7ė¾_Ķń‘Œ›4‰ÄÄ$©!Ä ”V–‚DDDŠÆ_?ʎ Ąßļbśōxā‰žLœ8‘C‡šī»ļŅÆ_?Ž?^ę¶›4iB»vķh×®5kÖÄĘʆššpڵkGėÖ­™6m””Ķpuu£zõ¬X±€E‹ćć拓“3¾¾~Lœ8³Łœ’·dÉįä䌟_}–/_^dæß|ó-ŽŽuqss§sēĪüż÷.9āD… ģģģprrĀÕÕõ?;99agg'=€…B!īpŗ®—žźßśUWó¾)ī[¾äK¾äK¾äK¾äK¾ŖöW1m¼Üöß5w½rķNQ–ś~¾L{"ĪĪĪRBˆ¢Ņōžå—_茵+kמģ>Zd™®]»²~ż¹÷Ž{ł÷ߣ“iõė×’O'E~žy=¾¾>̚5“ģģlüür¶×ŖU‹}Ž££#[¶leāĉ4iŅ„ž={ĮšįĆ5j]»ŽC\\µjÕ*²ķ 0dČfĪœÅĄٳg7ÕŖU“#O”›¢(  l…B!D¹ßć”ro‘W]¼ęZÉēłäŸB!DÕ¤ߖS®nåÜ n*WŻ_L;Tz_ŸZ5k2uāxŽū R!Bˆ’¤ŅĄy={Šu'"ńńń)²ŒŸŸŸ¾Ž9zCöļļļO‡ŠÜ@@@ĮĮĮ¬Y³†æ’ŽEĻž=¹téŗ®Ó®]8ĮĮĮÅn³iÓŗu»OOO:wīĀžżū —#O!„BqƕžźÅż ų$øŲ{‹ŻœB!„Øb½7ē†ÄEBa„črŠm“J\ŗu߯ęxäIƽ’~žmQŃŃxשƤqļ2ś½q7o8h„ŸäÉN iXχäӜ<ņ'?Æ\˶‹ƒĘźo‚ŸĀąµpOŁÉÓ§óÕł†„<ł/“/ømy“•< āŽ3hą–|łU•ž*Eœ™™IrrŖŖāā₦ide™0E–³³³#--gggE!99ł¦•kķŚuĢž=‹cĒŽceeEJJ -[¶ ,,ŒŽ;ņŠCŃÆ_?žžłƒ`oooTU•Oī!„B!nŠāĆßā‚ß«SąB·źÅ¬K yÆ ó'„BQµ(Ź5 ;„H{ÆŠ5„p(\ē-WV,!p鎟ˆ¤¾Ÿ/“ĒĖæmō{ć˜<~Žuź0ŒĪ IDATnĢŽ1ņĘægPk4d"ć÷²ö«O˜r"Ķ3€¦=žbų‡!LyŸGTtT ‡2±ŃĘæ¹™ĆŁÖÓ¬±¼ēM&Ō_Ėø”æa¶Å˜f”Sܑś>Ü J‡Ą•"V%’ręĘŽæ¶š£āÄ .u°”ŖU„^RX«_Շ77č-6ōĶßFīmל,qŅ`!„BQ%ڌ…®”’Ł)łmD%ŁkĀą"Ap”””‹é ,!pń“Šōųņ/GEG3nҤæSŋV÷4Āüėx6\. )Z,Vn`ūGń@£Õ\ ’9]54f°ŖƒŽńō"ęDæÄ‹ĶĄlVuÖ±Šü˜WFmįø&ÆøsUåøŅĢ\§N.]ŗÄłóēqwwĒßߟ#GŽŠ¼y³üeŽ;Fćʍ8{öµjÕŗ)åiŠ ŗ®3mŚōéÓ C‘į¦O:ÅÖ­Ū ++‹#GŽąģ쌪J/7!ÄŻKuņ&øI¶ĶZŃĘ+™‡†®ć§ž'¤„%dǰmńnŲĆń83.;šō›Cé×øŠĒo³&ņŁöHĪĘ&’Ž- Ć8tOŁ£ µf4Æ/Ųɹ$NŽ“čõ ļ¼ŠČ[’œ‰K";j5ėA’ŽFö¬ŻČĪćq˜ŖyÓöńŒŒc^Ū““2ÉC*ÄA±r¢FÅŗučÜæ?Ŗ!·żVÆjÜ)ÖĘ\ Qƃ»üU@‰sžź…āŻb‚ß«C_]æfż„ņo.ø( °B!D•~¤+…Æ\DÉi¾½˜0øpLīŠŠJq€óŪ¬_+/^¾d1@~ų{³ę’Õ,hX7†c«ć0cwĶż–ń»ŁӋ¾õˆš’Ļ]YĘ2‡·č=7›Ų›ś‹¬ß¤ļ¼s¹·Éć*ī|U5®4phh3öķŪĒĮƒ‡¦U«VL:•śē/³sēß¼żö[:t€ą›Ržąą`f̘ĮĢ™3™?>F£OO||rz ĒÅÅ1{ölN:…„„%!!!̟? Ģf³<#ī±±1R ā¶Ź™3=‹Ė—/UŹņ±%ƒŒ Hgß'o0üן zóFy$óגé|0|5¾MøM ‘{÷sÉ÷E¦Œj„1ż [—ĶcĘŪFź~7š¶¶ ®MzńśÄ'š°×‰Ūµœ>ĻĢ«˜ŅŁ…ÜõėæĢō± ±LŽ`åĢO˜śq#~ń¦¾dĆ„ķ‹˜:o< ƒ¾aDS˲Ėd'ǘwMÓțˆ£šŌ#łį/€žAŚ KG'Ŗ•ó=ģåĖ—ˆ‹‹ÅŅŅR>(nż‘]ʰĻ%…æ:z~š«īõ« v‹\ÖÆŽzq‘C!„¢2»*|Õ ü¬,“s{Į<ĄJ~ąÜE(< “‚®äöÖ zō–į +ŻūK,SHK/žqP“K$$[bmm ¹ń®"‡)+«Ź•¹ŅĄaa-Y²d1»vżĶ€ż©Q£sęĢ`ŊåĢž=+łżū÷”Ŗ*”””ŚĻ“iS‹\7 üłēöb—4či zŗ„ņ†±oߎbļ+n›ÄĒ_”gÉĘÓÓK*AÜVš¦a2™ˆÆL°ŽfJ%īō~žoŽOœqēµ&–čɛXöżEڼõ)ƒ»8”ŽŗĄö‡±q’pĀ[ē¬mW·)m[ųc -Ü/°ćŁ l;’MŪP Ŗłµ¢Sī^:pā·'łń`ę΁ł/hvŽMhź`¼ĪlbŪ²ztīŻ‰Ö–@0ģY?‚}Ī”5­‹’¼„ō2µ5ŹA&Ä ;ņ>žóKžuÕ„O hƒg‘¼ÖLĀįßł+΃Vżü°)ē¶]\\ńššÄh4J,*Åkp±įļ5½~ Żvu¬7,t^p,5,„BQ5›‰ÅĶżKN›Ōźyó’ę¶ūŠĀ9įm‘E õV”ņ…Ą¢(''G&—=*:ļ:u˜<~ÜMé¬d„‘¦9ća_|Ć^W°·Ė"-%sbBäYńĶ·,_łM•+w„ €iŃ¢%;wžEæ~żŹµNXX+œœœäčBˆJ"ó÷wiŻāŻÜV„· ^¼ūŃĖtvR0<ʉō+œ›Ųƒ–ļē­”‘„bŸVģ9eµFmj*I$&k€‰³[–šŃ²MˆŠ'Ćč€eš‹ ’>‘Øāęį†r%‘„ Ą0øāį {Rrö§E•U&£¼Uā`ØÕ†~ķź4|-ģp)’ÕfséŠ/¬Łš„_·Ž4s3H„‰JÆŲyK ‹ ~ õ.ųź× ­_³«¢W%B!„ØĢ”«Ļn(Eš9K(€®äfæJA œ’“"”n~<¬€®—/–^ĄņĀß¼ł"s3†7FŒ¼”ūT³rāŒ;ŻkcÜsłš>¾f‡B¼"9z" €…ČQUĆ_ØD0äōø½té‘‘'JžĖ*÷Č××ÆÄŽ¹B!ncó!|6“ 6é‡YüĪtž²ó#Ō×® ©Æŗr’»ńl£Āኊ­›# —Æż{o0`@Ƭ¹‚‘c¾Eķ5”‰ĆćJ4«Ęesi/r–€ sŽ6Š%– kZĮ©źRĖ$„ø#NvX9ąįéYĢĄfm`ÕÖ$üŗ÷¦s=;yī‹*Øäš·`ČēÜ0W×Ń󹂹7Ƈo^P\°©‚žĮÓKą+„BQÅZ‹W½IŹ q¹¶K/ynހυŅb ÖĖėł›?g°.=+bܘ1łsžoīĶŽ-Q¼öŲśü:“Æ/Ї¼{?@‡³Ÿńf¤<@BPµĆ_Ød°««+S¦L–£J!Ŗ(„š ŌĒ?ŽĶSÆĢeģņ`>Ņ‹Ś~ųæįX“F­n¾×1ZéŪĪ<ń/'ō`Ƽša hvŌqųoo e•IqĒ3ŪĮ[/SÆ[ E•QڇeÆīł[8üÕu­hÆ_]ĒlĪʔ‘Nv– ͜žēģˆk;ė+B!„øķŠļu«¹XēōVÕ`…„£µ ƒEĮĄłķB57ōĶŁFўĄ%·e„0Ō÷óåxäIƽ’>Ė—,ČļżĶøI“nĀ^527ĪęƐńŒšō6N_üĄÆ’ʓäŲ€ nĻņRŠ.¾šü;Ē4 y҈»^U”’ĄB!Ų5Ģų'w3hŃ|ŻnOÖėĄ€‡–2äĖ1Œ2 ¢g°'ƌX"“jŃóž ģŹŲ¢±®/uōoY³h=]}q1Äq>õ?–Ņ©Œ2)±?aąŒ3<8s1Æ3Ē–½Ä Ė­¼š#÷V4žœž4omfücčģ$od„Øō"vžCZVŲ_įāÅ+łMd;gg줄,ŖĘ\“÷o~|[tŲēüšWĻC×42®¤Rß§ļAĶźž 9'„B!rZ“ŗŽŁlę܅XĘLų€§Ī`m[ EUÉé’«¢£‘Œ­ ä·H„pIčŻēšŪ¼ėŌįģ¹sŒyoü Ÿ’7’}‚?§½Īė>E’ŽdÜ`;ŌųHNžŠ)#6³/YŽ Tłš$BqÓXšä0łķe>Ÿ»žn>H‹×ē0Ūéc>żi&Ć>Kƒjž4čņ2]ŗ—dŅČx¦.Åė+S1[ŚāčZ‡ĄZö’į-„]éeŹßšÕ“3Ū”.= „Ør“xbā²ÉČŲʬӅnW] {ģ Ā=T©#Q…č…G.9üĶķł{%%‘IļŽ¤K‡6˜ĶfĢf3YYYRB!„¢ą­‘ŖRĆ˃e fńėęm¼ūž lķr{ kŇĄ(£IKš[!gĻćķwŽ%!1ń¦īGŃā9õć Ž’ńź{ O–ÅÅ„Ó½ČżYÄ,~”䔢JPŗö¬=a7XĮ tM×ręLŌu4͌®ihš–ūÓLJb<»·mw­[§{÷„"Äm„i&“‰ˆˆBCC„B„āŪ»w/žžžFTµbqćŠpģÜPUŠŖ¢ŖjīϜ^˜ŠŖ¢**(9ĆÆ)ŠšsžEQP ĘhĖiŌKÆĶ»FŃ]=÷oŃyóēłÕõ"įozZ ļæ3ŒĪķ[c2™¤R…B!D©EĮŅŅ’æ’Į{“gccgŸū%ļ}Š’’¾%ļżJŽ{—œH˜"ļ_nå{˜ˆė©]·n„®ßu߯ęčńćL˜<å¦õüBTmgNŸĘ?¬{…Ö‘ĄB!„BQÕ] ÷•s͜E£śuéŌ®•„æB!„¢|MM]Ēd2qO‡¶|µr5Qē.b°“„B³DM)<ü³Ō_YŠZ!ž+×Nˆ*ŠĄŹ’/33³Bß&“‰¬¬¬œŽś24­B!ĝ×VĢ’æpo`rGt*Ü X]Ē”™Ī{o•įž…B!D…eee1nŌ0L™éEژzžw”6Ŗ^ŠN•³’BqėI`!*9³ŁĢ…Ų³$$%@E›KŠ‚AUqwńĀÅŁƒĮ CD !„BTQÅ ’œw±ą6=žßœómy!pĪśŁY&¼<ܤ2…B!Äu©īéFv– ]×sęūEĻ›˜ņē¦ąæ¼^ĄWĶ ¬ėŗœ§Bˆ›H`!*1]×ÉŹŹāRB<ĮĶ*Ü(Ņu”ŌdĪ^ˆĘlĪĘŻĶSB`!„Bˆ;«ÅXč’¢ķĄĀC?ēõŹŠĢęŪ‚ŗ®qśō)bccnłšŠF£OO/źÖ­‡¢Č@UB!„ø­¤J޶‰‰‰ĮĖĖ«ŹÕ«ŖŖhfsĮ‡óÓŽœnkŠ{‘± …ā֒XˆJΜ۠R“)³ĀėĒÅĒPĻŪ—ÓQ‘hšŽ§‡—„ĄB!„w½š%żŖį !očüo]×Ń4­Äķ>}Š””Z“h…Ķ-ż]ŅÓÆpųšANŸ>M½z>ņą !„ā?“¶ĶĶ“7ݜ’ßĶm*sĢœžJ,„·–ĄBTr…OŌ]Oh›iŹĄĘŹ–zuü8u/ź !„BTéFāU?ó‡&øēœ[õüŽĄłw– 66†P22ҹr%ķ–ž:ŖŖŅ A#öļß+°B!niŪÜäĘØ®£+zžŠĪ J”a”s#ßb‡–cS!n €…Ø M*-ļDŻõµt]ĒŚŚļśDž:†Ŗ(xꆡāĪqžüy©!„āīh–rÜŸÉŸū7ļ²ž??pńL&f³¹Ō^Ā7KŽ>oõšŒ’ĻŽ}ĒGQ“’ķ•ōRč!”CčE:‚ˆŲ)" ØˆÆJS•¢4)‚€‚‚+¢ „ƒō!B ½÷+»ļ!!H ēū1’ÜĪĶīĪĶīĶī³3#„Bˆ{—“mJ°5šŻ®ĢžĒWĖjƒ*JöœĄł ]š+B!ŠŸ€…(T-»0c6[nś£Ń@@@QQŃ\¾ō;žT÷ÆÉŁógšp/‡N§“^Ą÷ *H!!Ä!… ī"ķšß®ž™¼7ēr~nĄjµŽµ½¹›ėB!ĽIŚ6%Õ Ķ;Ķd‚µė‡ĪÓb•ūBq§HXˆ²Š¦ŹÕ8  z‘ŽŪ®UǼ½Į€ÅbĮb±`kk+…+„BQ¶[Šy»ēŽ“3tĪ0К˜B!„øżhĪ#Ц¢]ķł{ķ@†¹ęBqēHXˆ2 wą  ³X,7ļl0ØQ#’`±ŖZo8üß½W€—ŁüŁgģÆń#ŗł¢ÓbŲ±x[Ź ęŻžÕŠ—į}ŚWżu^ļä!‰Bq»¶ķ¦]Ÿ §soN W»&­ĘM{ßWmD!„BÜwm(Ql›Ó”7'“«v嗬ł€³ē.ø°–=„“Bˆb'`!Ź€«s•(ŌØQ£ó»OXϳ~Īlv }·»ł‚Īß g²ź‘ī¼Ó³ZŁŽ§—ū3LĄB!Ä}.÷Pй‡€Ö®}„°B!„(Ę֧ƕ޿9Į`-gč¼óKļ_!„øŠ5.%*DI4Ŗ® ­( §OŸ¾„9D<==ńńńɓ_”Y/ńūÄaŒ^“‰ 7ź>ö³ē¢½—R<ĖÆH9²ž$el0õ•¦”B!Ä ‰Ü4 «åš8O·ą‚ŅK/!„BÜKĶ%iŪ”hCTS²B»Ś5³żę“ZFBˆ;¬XĄåĖ——÷  Ówl]ŁC@ŌŖU«Xó»93Ē>éMļ™Éōšŗ’åϰdĢxžźēŹžC©¦»Żå`?Ās>䃣æ”ŖēA©^B!„»iŌ÷JoßœĪĄŁ}4 1B!„¢ķѬ Æ–5Œ3W Q²ēVnܖ•@°B”8e MuMą'NłēōéÓ9sj©pę6ĪßO…W¾ąó×»ÓõéQ,ż“/ö’ĢaŃ~Ėķ/ĒŹłeo2j£ƒæ]Ąsī…hŖ‘l5vÕ=±·±Įѳ* Ś?ĆÜWęFĪ8Ćw#„ž#¶^Ō~ų6Ē_»Ļ*1ææK§šžŲŪ:Q>š ŽYLföbÓf^«hGė™Įd‡ĖÕóshg_‰a[L %shńK“ šĄĪh‹K…śōż:„CėÖ¶LļG«jīŲŪ9įU½#“v^Y›õ2›>īCĖŖ®Ųٹį×ņy¦n'O?ļBķ“B!ī˶bīß“k–hłM|ƒ¼4ķ®ž!„(›Nœh¾eōK«Ųj…Ę©üłö£ō_åŹ€Ißši]#—v,ćć=ZĪś³[¢é™ž<4f1c+«œ\5™÷Ÿ{ŒŒ?ö1§£ÓĶĖåŲl^¾Š,ēŸG* Fž&±rłžŖI翉ńÄl3=?X̤fd†ĒąęoŅŁóž£<9O”ĻĒ+˜ZOćŲņ÷ūÄ椒³ƒ Ķķ@K,Ä>„ˁ"„BÜ“½~ķ=¹ÜĮąbb2›łćĻæųwŪĪž;‡Ńh¤JåJtlזGéŠ^ÆgŌ»c9qź4Öž Båŗ=3š°8 żń±+ŻŪŗdŁW,żz9 2kŚ4ģķķäā¾h–jhʕ޿\éŌ{]ļŽ‚ŗūJ7`!„(i¢L“§®ö>yņäMē¶±±”f͚7ĶÆPQDćEyożÕķĖSĮSćTx–Ū\®RNWąp–“$RMjÖĶKÅG~cę¼cŌµ—ļ4ÄŠ¢‚™£[•µo±ėųlÅ%ZLü“/†ūgåŪŽĆ_żĘŽ<9ė©Ü}$ļ nŽčڹ>'2uĮoLčų ®7+—˜(b4wŗtx;M Žfm=ŸĢ;Nż±‡X:¢vžÆ’ Ÿ,8Eƒ±‡XōzmōĄƒķj|¬ ŸĢ\ĻŪkzįRˆ}Ņ’žŚÜżŗšĀßß,cÕ_’qźRfŖŌmFמĆŅŹK†B!īĪ7t>æŻ^;16.Ž “§p.$$ē5³ŁĢ©Óg8uś mŽĀ‡cßåÄ©ÓEn !Sź9žžv ?o=Č™Ė ¤+xV A«n č÷Õ,Q’±źĖUlüļ —Ķ\|ØßŠž/ęŪ\ygó÷ßņóæ9}9žtģ)W¹&Ķ:ōbŠó-)Æ/Cå’üļöžŹ6KM^]:ŸžUJļ•ŌŚuæ°ōėå>r”Qļ½ĒĢ©S°µµ• .JĒńT„6Čć={*ŻśŸ¾—r½Į_¢šŖłłQ¹R„ėF̶X¬\ %ģŅ%)$!Ä-“°e@ī»uźŌ)Öü Ó¤ÓnųDŽķ.æ‘LžV'¾ŠĖjJŚõfĶ_Õ8˜Z‰Ē»ŌÅ&ŸwX‚ŽpŅT™­«-ŲØ õåIßt˜³–ghz“äĘÖCŻy-#d_Ÿ—yõµĮōhģ…!æmžÓƒ©•éŽ!ąŗ“®åō~§e-˹aØA‡v™šŪ>ĪXzŃØūd½pś®×S-łó†dŁEģńcTĄ>3–sG÷‘˜nē:…Bˆł¾ž÷¼£@ķ†\an’Z,>œü1ēC.ąWµ*Ć^J£XU•#G2oĮēŸ;Ļď§)_!Ä}v·“OŽžÄŗĢ\ē­d¢ĪbsJ%Š2޲päXքZŠN‡5įĒv ÅĄ”yņšłö$~¾&ÆČ³ųÓŲ„ž}[–±s»‹µōŸ7·ķŲIēN²ž·œ<uMząą!Ž?AÓ&„’‹Ņq8•@¤“¶kZwģDƒĄśL4777“’xļż8xč0;’Ł\’§Iz]o`鹛Æü‚惞j~~BÜ q6V‹’ŸĪ»<ŽDe½zŹČˆąrœ‚wyo ·¹üĘAZZł…-/˜³.āuŽŌäK¬0 h5*YĻižćģrQUPtYķQE‡^³9’ēm¾į4Ż6-gž¬Ł jž s'żĪŸļ6½~›uKoš,¤vóē$ ³Owżā#ƒƒ‹?ę« ¼śł\^®cŸ³¤S×§®&ScŲżõęż““3q ž5Zó̰’1°y¹¬ŗ F±iö$o?ĖÅØD2p¤RÓ'čŪцżė’bOP&§Ŗ“é3ŠńżāŖj4›ēNfÉÖ B"ČŌ¹P9°Ͼņ*Ļ6p»’o,ŪĻ`ń¦£]NĄd,ǃ£¾dŚcåŠY"Ų¶t.‹~ßOP”:ųö[ō®ć„¢„qzݦ|µ™ćŻ+ÓaŲ,>z¼<ŗ-“Ó–Bˆ’o!޼e”å““š æżń'ēC.P„re¾ųlŠ¢a2eŠØA/üŒĮC_#(8X>&!D§°žłl&æ„d¢øŌć™įÆŅ»eål2ˆ9Œq•hb–Ć’²9Ģ‚¦÷ć¹Oę0¬± ¤^ęų±*ųėóäµ.$űO¾ö*Ļ“ō§œ>ČsG9M#*e7ŠÕX|’%K~ŁÉÉ(åėŃ±× †>Q°žcķ“üz2ŒČųDR2œ|ėŠžŁWxż±86 €5–}ß.bńÆ»8cŹb=|n(Æt Ą!' ’ĢÉ ĖXüć6‡%¢9śRÆē(fd_BYN³pĄC,Œ _ē»9=ń)%”E_.å«ߊżńĒłtę Ž5†'OŅżńĒiÜØ”ŌqQ&ķŻ¾µPé"##JåöūW«Ę‘£Ēžöę͚‰¢(¼1bAgĻRĶϯ䚢ŁĮŻ\Aެī 2ōsA:“k›ēļ·mæn¹N§ä¤»v¹B†€…7¤÷€–>Iü½å ™ŻĄHپ‰Żęj hYćm.æq LĮ£v:äšöX‹¬C żvķ EméŻū µšŅŠ~.›’:‰©Uƒ|{ ē+ó0żs£¦Ō0V/|½4.œ:KµqĢwóœØŽå5fw@Ƒ-čōŁ—ģьĪ×nsLCźŁ~ʶƒ±<wč¬ķĒ¶Ļa} VV/`K[·_Ā”aSj ·Oś*5īnEÉŲĒŚ—ńxx$rÆ)dŽ~ž&o|«ŠmŲŽō×8»į ę½ż6_,įµŗ6@2Į[c3Ęטt‚5³ę3õ³Śō|åu¦¾jOģŽ/™ŗ`‹æeTc#DŠˆō™ĒŌĘ6ć2{\ʬįA$/śœ—kŁ ßŗ°Ź/ńŃ膸X“ŃWõ@G:ēæÉČ?ĖóāŪÓy×;‰ŻĖf0}ä\*|÷­ĆW3~Ƽ_ś/[y”Ę] ÅĒ ¼L!„(u ų-̃‚[·eMBńźĖC0›3óŒ.3ņQ9½ĄŠšÆā>:%Åmē— ؊Ķgx—ģ‡ƒm©P§ ®¤³:9ć¬@¤ĒéżG¹X£~NhвB>yŁŅxЇŒz,ū:Ó禾TĻI˜Ę…#yė‡ ˜1`ļhCņÅż¬}‚3‰sXŠæF5’£;r* lŻq±O%>ƒF« IDATģ ėfG绌QĶģ”0iHćą¢QŒų.‹Ń•ņ^¶Ä‡īē‡écˆ·YÄÄN(dp|ŁhŽXqšttŲ:¹`—EŠāŒ1ēšÓ7_o\ `šv¦“ŒbüX·~=³§Oć— xžŁgP īˆRt¾)B$""¼LļėgsfńŹėĆ >wžįo@QĪŸ£BłņĢ›=³äژrČߖ“““ė^KOOĒŽŽ¾DÖēŌk)ßōu½ś]s…!v%]‡ütÓ÷ėl°ÕŅI7_=¶ŗLżšA'ĒŠēė’{8ān­WÜæ^ģߏeWŚ;e•€…(c 3šµE”vķŚčõ·p¹hŪŽW‡5ałäW^ż#ž+ĒQ«Hļ8‹—š@w›Ė‹HńīĮ«ĻLąéIżxĖi<½j+œŪš-G,š  øwgŌėué4õižć^n_Ū”œIŃ®k”&ü—?¶¤ć˜~žĶŸO`ęłĘŒżŖĪśZ<޽“¦½Ć+2éߊĪ%ꏿMkšz¾ųG„~ƒŹ8š.ņωxš(‡G>Ń?„ÜS¼5d ]'=Ķmż؀1!„Ōj½éŽšIF«M§ŸcØÓDśÕÓ8ŗ|>©F’ÅWæ„bV¼yhģ\ŽļģFųŚ÷yiŽ^N~’-;zŽ£cöÓ¶JyžžśĆėÄšćØAĢ:Ķöķ§x³Yć\ĮŁ‚Ó裒ę«u°ėņŹ¢Yō÷3¹ž=śĶü­æn%śĮ§(ū7K¾?CŗāF›7f3±{UģÕ4RĶöčL§²Ö”÷£Ļ“Ņ5šĀÅKX±rUž×Ö­_σŚÓ÷¹g„r‹2ķÉ^…«Ćæüšm©Ü~w77>’l^N ¼Æ/Ÿ6OašfĻ{h¹ŅH“ųZ’nŪŽ­-Ƽ4„ömŚ ŖV6mł‡oVÆĮb±\×SøøŲĮĖӎ“{š Ķ\ˆwŚPeČJfe¼Ä³KcČ~Gčž=lK)Į’ŗ[ė÷³^={”é °€…(cŠcą¢1Rä÷|—:ŒŃć{³<Օŗ}ĄŚy/ćÆ+ŽåE¤xņļõ|į0’Ł>ĒńöŌhQ£¢Cư§å„ߣčł.ļ/|‹ž“ā±ŚyP©F{zŌqĶŗ øSÆC[|~™Ę³Äc2xąß²;Ó’˜Ā°&¶9§Ē†£W³,öu>üØ?kā­Ų¹—ĒÆe'ūč1Gį§sK¦ŃjMŸ`ÖŅ‘4Ź÷¬źL‡i°Īs4|ń=ǧaōŖĶӟtꉆ•x`āÖ9Œ`ģ“~<> åŻ_f1ŗEö“~ļÓSŁū”å;GÓ®4ć nČ[/œāt†76©tõ¦Œ¾2Ķ{ńłŽ“\°v¢Žuo×QĪ»JZń€Š{āķ ū“S īĢdS“ÖM=Y¾ļ$”ÖöŌQ ڦ3œMOćҤ'h19ēÖ³ۘTōŻzņb󙳜óļśĻōz’εÜц/B!īō÷pß$«›ē„Ļjt”ZÕėŅļŻžo¾ļ —ĻI‘ē\”ļŁĘrŠoŽĆ/ńF:~°Žtć×°Ŗć~Żš'm;Ä©-«ųxėlń)S­ŠRØp‚õģIN™4tnĶy¼½7 ņƝiøš?¶„œįxؕŽÕÆ=įyQƎ7ŗ”$Ę%`½rIr³4œ=Į©L M;Į°0÷Nd8‘Vp:ʉ [kž~¬*ö  wĄQš©t~j‹—.».ų šŅ iѼ™TkQJ›I÷ĻĄš¦66WĒŽ3((%°ĶZQ6 dd€½ŽWŠzŚæµ”Ž* ]%žž³‰§ēÜdEvµčóé_ōł4æ…ļ±éŌ{…/CceŗŽ]M×±ł•OEŗŒ]C—±7ś ņß'UU1™īžŻWe*Ł[9tę&šŠ ųÖZ•£0aÕ®<)Ŗ1²ęl¾QŽŠN)Ü q'¾’)ƒkēŻźp(ēŠbćNŸ9ßÓfļ¾]¹š¬dÕ«ŸņÅĄ:ŲŚ¼LNUB!īažÕŖqģų Ž?AĖęMó,“@Æ¢P×å+SQÆi¹ČC1 šó¾ĮT*F¼ė?Ģąś3hx릌ā“m±ģ\½Ó]_¢–oEŹėĀ-”ģ?ĶæzßbģĮ`4f_|xżq]UEŪ:?݊J¹.5—śx믦åśķĶ jkØ„$Ī“xé²|ƒ/½8ō—J-ī =Čv­ŅŚŽ‰‹cų[#¹p!gĪßó!!¼žÖŪ|6gžņ!—B­h Ąč±ćPUO¦|ÄC>X¢ąÓćŠx ÆōiEÓŹöć/pqĒ&Æ:‡ķĄåĢyHÅČB~|ŌBāwCč’]*Ķ?ų•”ūŸdČś¬/­§, żį$7yfõ˜/nē·%ėŲSūEu©Gķr™$ł‰/?_ĻīD#ŖMMZ>’½[TĆĻƀs’?,dŽ?ń¤¾/~½ķn#©i'šW¶E¾›?¾ü‚ÆOXŃĶP‘ŗĻeH{üģbøøwējÖ%sśh¾ø$wóDžŹrXĄB”1w¾picįųóل֤zEwō ĒY7żSŽWȼFF© w‹]3ŗušąĻ?¾ę§géSõś™ŠõUėPÓö{ø„X5ė&‡õ"ūEcW£6Uõ€ZLŪ£†qųH,¶Õ©|ƒ|õ•«ćoó-gBU*=@¾5H±§JĖ^Œjł]>Ȑļ~įPß:“4Žd™Bq·ÜFĀōéҹĒŽŸ`łŹUŌ©]g'§<Ė“SR˜šŃTĪóów«åóB\GēՆ®MæäąīT,Ģ·7éצn׆XÕö’sēśx; 7:SĪ-ė”f-=4t^-éP÷ Ī䱗˜m’*Ļ“¬†‡ŃLĀ„3Žń¢Kōu©móū’cżÖ(štv'üĻæ9dŃP«S»rńå£÷ Ąß°‰£ę2}:Ņ÷I0'D`惗jÕŖ6q,qæn§Q×ņŲ`!%Պ£­#N6 ¤GqīB*ZUg4‹ īĘ`ŠoŁ’o bȋyń…R”E)o¾]tłņå2½Æ#Ƽ›üżü³¹¼ņśœ įķŃļšõ’Ew®-*=~ ­Ļ€9æ7iŌƒ”dĒ—ÓōFlmmÉŻMH3›Č“jX<ŸfŲŪu°’ņFīK Ķ#€Z6‘$j .Ą›öß2+chž”˜ÆĖ_W…†uÖóŁē#XļMõ¾ćų`ZK:®’‚/ä¼”1ŻŽ|‹wūž ū‚‹(3–˜=ü¾p '.ƒ]ćĮŒ|łMžĆĀ vDe½õ“X°h$Kc=ØŌė=¦¼ł4ēßXĶæŽųöŹ”śėX0õö¤U¦v7Y!”MR ÅM”Õ °€…(cÖ“'O^7׌ /<õś{+h¬%²ū'f­9FhT2ŖSEźwĀŖ%ćie'uä®Qœh÷Ś[Uu@*;g dĢŽ†Lųj,ÜäI!D16›(™nc]:=Ȇp&(ˆ1cĒÓ’ł>4jĄ”#GY±j5—Ć#ØS»¶|Bˆā^tž;ƒg³5ś«>ĀjEAQ@ĖÕåÕ“ž™­"ÄŖ čōč±bU54E‡OĖVŌ2Jyž|c[ßžœ‰§ųqź’ų1×ŖtąÖd4­½:3ąń9üÓE6MīĒĪ96˜SŅ0cK­žĻŅÖ 0Ļīé*’u#§‚“5;£s·@ qgł{Åf…%b28S©N;F¾ß—Zz0Ż`Yī˟¼WCłÜŠ×JļüAB!ī Eł–)̃†™™™L’h£ŽKŠŁ`¦Ķœ}]šZ5kšń„QU«|Bˆ|*vcņē•X·ś;~ŪyœóщdØF=½©PŸŖčQmjń`§&l;ÄŘĢ:{<*śÓøc/^ź×§+‘Ū€ŽĢü¼*߯ś‰æöž"46‹Ž ž¶h€‡ŖāH³×>a†ū,^æ—31f*4 mĮ¼öt­āĘEq£ķŪ³ł¤ŅR¾Śø—ÓįI$éńņƅÆŃ„†Eq§ćØY|\ńKVüy€³Q‰¤Ų•ĆßMOšRŽnoæĖÅYKŲpųńqą]Ė“•»r'ļąį#yžī×ē9|A*±(ŠŅ‰ānęYģŽ‰¦i˜L¦œū .Īά^žŠ¢‘‘QbmM ü޾×_ŠŃhdĆĘßYśuÉ’ģph}'":Ÿ › æńżÉ Œ˜9Ÿ†ūw°uÓF~;’ĢķŌ}r"ńTÅĘęź -Iɤ诔§ŗŌ¦E»Ö“¬ķ—«'å+˜Po³'“.#ųL[| ę ŌVŽń[Hīaū¤ęŠĀYõķw¬\ómŁkoĖG'DŁR”9€ķķķ BwŒĪ£!όhČ3# JąE«A“i5Ø å ]¹”¹ž>bo§Üiü²|CņŽaĮ­õk,V}!ó½śMčC›Į“h38Ÿe ²č‡łnŖķ –ŸG§šē£yš»Ō|a1’ę¹OāHėŃß³]ŖŽBˆR¢0%iš†^ưhž<~ݰ‘?6māÜłōzžÕüy¤ĖCtķņÉÉIØŖ<ä$„(˜Ž3žÆŅóõ‚R“愱ķx©yŁ”oAß-č{ĆzŃ¢ß8Zō+`¹±ļoų›÷s½T}ąb¶ ,bƒ7-ž‡Ļßh£+ŠnąxŚåwYįӆצµįµ»üEEGséņe<==iŽ“ m[·¦SĒRyÅ=նɶgŪ?…JV*÷5æÆ¦idffJE(¢£cˆŽŽaĮ¢Åwu;tęÓl8Ćõ:ÓµS[:~šē~̈‡øpĖĻ>ähĶ5ā‡s{|4€Vū¾`é÷gŠq£ń{sésūg€\;¦C§ÓcŠÉõ‰(š²ü QęČĄB!„¢¤ö&©Åb!..އźĢSO>Ń˜õ$½Łl"--„„xįB!īzŽ•_-„šŸŸ†ø§Ū6/^”wĶč±ćJŃÖdx|ßߥźuÆ0mś“tõŻĖ¢ĖYćečtÅ;#}fõyÜęWę~³—V'ņėžt;ė5D„L{Ö²°ī°^*œ(”²üŠÉG(„B!„²n’öGUU’““ˆŒŒ ,ģ"aa‰ŒŒ$99UU‹”WöBˆŅÅÓÓS‚æā¾iŪ”Ä…5żćÉ̘ņѝ9.ōFlmm±³»śck›µø4„CĒŚxŚb°õ¤B€7åL‘D„č+±1‰ŲŌnIw=¶¶ÅÓæPIØkīX o*µėĶć~¹{®ßžz æóćvoZ¼ō2=j:ćģęG`Ž4×_=N¾2ƒO‡ŌĀG_ޚΜ~~øėóą˜iĢ|ŗ¼TŅūLYžB)ė×µLÖ«Y×2ŗ¬k%ėZFQtwķZęЍT.e#th×6Ļß ‰‰œ8y €ŗujćęš÷śäßmŪå€.ęr˜8Æē†Meeœˆ(Õ.†„P·e·"½§ŌĪģęęN’žżhŲ°!Ge͚5DFFŽV¾ĒŽ£WÆŽŒ5ŠĮƒ˜Ījµ2uźT–.]FJJ <ņ3g~B¹rårŅØŖJ·n²cĒŽ|ó°µµåäÉLœ8‘~ų‘ŌŌTŒF#>>>“nݚwß}‡źÕ«ē¤×4~żśsäČaöļߏMŽ“ĪīŻ»éÖķQV®\É£v“¤¦¦ņŒ„„GF°`Ń"^2ggē{b’EĮĘĘæźTš­„Åb¹ķ< vvvF¹Y,„BAv_«ÕJ­ZµØY³ę-ē|bæj)ŠöŃ>hš&ķ¢œkéi.„BiŪq'¹¹ŗŅś–RwZÕjԊßĻĘD£”…ø'•ʰ»»Ÿ|2—œ×ڶmKƒ 9rńńqEĪ3!!łóē3oŽg¤„„Ż4ż§ŸĪeīܹLœ8___ʍĻąĮCX»ö§œŽ:Ž3¦“˜˜Ą_,āĄ,X0½^N§ĆĶĶ‹ĆhÜø1&L 33ƒąąsĢž=›īݟbļŽ=8::YĮ¾gŸ}† 6°{÷nŚ·oŸg›~üń'¼¼¼čŲ±ƒŌ\²zž~¾x)Q9ÆEEG³čĖÆxå„Įw¬'pIÓėõ8::bggWl=€³ė§B!ŠFUUĢf³Ä=DSÕ¬a·%œU2WB!¤m#D‰’½w‡©b{zT fß±hŻŪҧOuŅ’˜Į!«\ Š{S© ų...9r„%K– ( C† !00^Ąœ9sŠœēŠ+ųóĻæX¾ük† yé†i333Y“hƒa芬Y%mmķxžłē9xš M›6ĶIŪ Aƒœß×ÆßĄÉ“'iŪ¶-ƒ!O#ĆÓӓ-šŠ®];ÜÜ\yńÅA„„„PÆ^½œ<:w§'?üšCžpFFæžś+Ż»wĒĮĮ«ÕʬY³Yŗt)QQQÖgĀ„ tčpŲd2QÆ^}†7Žx€ 4k֌Ÿ~ś‘¶mŪ2fĢ;üžū‡YA÷'žx‚U«Vrōč1\\\6lo¾łæœf³™3f°fĶ·„‡‡ÓØQ#¦M›ZāCŃf÷üĶüĶqOõĪŲźõz9c݃TUåĢ™Ó\¾|‰Ģ̌[ŗ1›Ķ„†^dü6')P!„(A𦢩VR"ĪįRQ†‡ŗw>×+3-K/ąœz.„B!m!Ä½ĘąX…ڽŠ÷-{ qAŪ8’ŃҰHш{µĪ—ʍjŌØ .$&&&ē÷ ŠØQć[ŹsŲ°a >“ÉtÓ“!!!DEEåéiŪ¦Mk ūöķĖ.*UU¹x1Œ5kÖP¹reüüŖåYīččH=X»v-S¦LĶéÅŗcĒ¢¢¢čÓē9&OžĢĀ…Ÿ3a‡ԩS‡U«VŃ»÷3lÜųŪ-mŸ¦iģŲ±ƒĄĄ@.\@||<ļ¼ó.ļæ’>ļæ?ž÷ߟߒƒ &вe Z·n Ą‡Ną‡~`Ś“©TØP‘O>™AŸ>ϳoß%|½vŲēüDEGół’/ļ©į Å½)(č ))É“nŻ;;»[:§dĻüū6)O!„(qšréŽūLŃșų¾/é%#„BiŪ!ī=ŗ3ß0sō7Rāž©ó„q£²‚žVė՛kŁæėt·vc¦(ĆŻFGGąåå•󚝝DDDÜŅś×­[‡««®®nŌ«W-[¶0cĘō|‡)īׯ/qqqlŽüwĪkß’=õė×§aƆ$$$°hŃbFĶŠ”Ciß¾=Ÿ}öµk×fīÜy·Uö5jTĻéłūŅK/a0xńÅ銔|š>ģܹ €ųųx–.]Ź„ ņŌSOŃ¢Esf͚ELL ;wī,±śńå×+nüĶĶW߬”£\”j—.]¤~ż·üBqhWś‹Š{å#ĶłŸ°±±Įb±dõо ?‹é]/„BiŪ!„·«TO:dČ\\\qqqeȐ!9r¤Lt§NŲµk'»wļbƆõōģŁ“~żś³yóęėŅҤIcVÆ^¦i$$$°aĆoōė×½^Ļ™3A¤§§Ó¶mۜ÷ Z·nĶ‘#G°XŠgŠ‚Š+ššJzzzĪ:¼½½ILL (č,iii¼öŚ0<=ĖįéYކ a2™ˆˆˆ,±²¼v±ŠiC/^”£\”j™™™üB!D©ąććĖåĖadd¤ßń¤é\¾†Oył „B!m!„ā6JóʵhŃß}÷-͚5²Ąļ¼3†7ß|“eĖ–1yņdŽzėMāāā˜>} ø¹¹Ń¬Y3¼½½o¹Ü>śh2žžž|óĶ ¦L™‚‹‹ =z<ÅsĻ=W"`M+]!yROÜ=Ö [XuŲ“.¾’ąéū«#-»S÷hkC({`Å@Ćīt¼Ā÷gĶł§ŃŁ1 OEŚ„†3|[&Å@M'źžC)ūtķöŹa*Ź€Ū”cŁŽ)!„B!„BqĻ0Üo;lccChč…<Æyzzrüų±<ÆéõzʍĒøqć ÷“iSÆ{MQęϟŸ§oaUŖT©Ą¦z½žŃ£G1zōØß’ŠCqģŲÕż²³³cƌé̘1=ßō;wīČów÷īŻ‰ķžgŪ¶mĶ“Ęh42fĢhƌ}G>?EQd`!HcĒWŸ°(ķ<Ü&Žu_’ ½ļnŲŃĒ•Z»ÓŹ×ˆ«^#9ŁÄ‰Ó±,Ų“JüŒĪ*ž|ŗC+"µ¦2wé%ž(Āō?6Õ˳Ŗ›[Ö]`~赿nŖ4«Ģ§h|µü"ė’Šq_4•Ųa)Öbčķ«§]×Ŗ¼UӀ¬•¤äLN‡¦šēįDöĘ«„l{…ø3z5˚b£lõ<ŅßĮ6q¼’s,'3s/rdÄK(·ė<ļ.­£#(xÕšaJWgw†0ņ€¹ ŽāP¶Y­V)!„B!„āVŖĄÕ >[č“Bqß2āļIŌTŪc‹9RŸ»ø9н3Æw÷”IR"ßn‰!4\ŻķØ”YI½Ówõ5+;ž ć¼mVŲÆ/C|ÓųüÆ$Ā“¬å—2Š–„«££ĪH—6nl‹#$WtSqt¦S;līö $ćk&6løČ†bŹĪŃA><–÷w¦aŅėšp³£Y]ĘöqåÆ?ĀXl¹½Ąm1oÆwJŁ ƒƒÆ'ļu13ź·$"ŹŠS®U½˜ŠŃ£EźŽŻ’žž.… „B!„BÜĆn)¬   Å;äęŌ©SHMM-TZGGGłō„÷-56”Špuu"!4Œ4īźöčĖŁSßÖĢßŪ¢ųéŅ•o†žĪHg¤y‹rۘL\!žÄŠ•sē­ŽŽÄī‰ą Šƒ Ž) =­·„GO\biØB©rwĶoæż*… „B!„BÜÊVŠ ŗ¤Hi !Ä}JKL`īfF·/Ļž™ģ=•ÄļG“8”˜Õ“S±sā©śl¹Čź+½gƒ’1ŠōOŚVˆbߕ©ŁÓāÓŁ1«ēčŃ#Ÿq”¹—8'Æcąō®ę¾Ņ«ģ’Š]Gźq[['z6°įü˜wŲ„ ¹lĀĮ³*=›8±vc2©×|æ¹Ųė°dX8x0žću=é^%¹!~ī4IMdt‰šB€ƒV“"¬#ś\<ßĻŚē3±qÆJļެ »v;ņŁ—›•kHįśŃŖ‰™œĖ„&ī6ģ 7ĢsX*ǬŽ4«¢gĆq+šŃžf•ąŌ®4’ŃēĶŲ։žløšßę0åéĮ\\Ū.DqĖ“”„ŗ§‘Åō}•™Ņه£Ń—ł;­Øē +»¶E±£Oy†wŅ“R͆ż›CŁVøAyŠŁéqAå`X:§¢U ³Ąk…*¼éAļČĄT¶»čŁgūJ!!„Bˆ[¶š«µRBQŹŻ8|ƀoīdüBˆū›FŲÉHžKķŗŌwg\#Nļ ēć½édøŁāgŌįó?kŗś.½LŽŗ|!R“LD¢ĆÅōn¶T֙Łn¹ķ”õīvų,ģ3_ŠY5sō²…ēżl©ØKęLīĪ·Šg[HKVQ“ÓųéŒļ5vaMø™'ė9¼;³V…@8Śeķ‹®0ėČoG43'#,ŲV±„¼.™³7ŁYżMĖÕZäņŅŻ$OŅSŁÆś;ā|<‰Ģ N46¤³ņœ횰ĮŻŽ€+å`½Ū.ÄķZ¹;–U{bĖÜł7čæ¾®X™—ŗøqśWsяµ“¾ÜžŹ‚®®ų„F2=ØšēZKx"?\tbHŖŌ8Č†#‰ģж^wĢ+NĪ l¢gÓÆń\°:©oB!„BˆūCܹh•L†;§£h*:MÅ^Ń”C¢Xi_ƒ5±v¤›uXudÖzkT*å›śńóŁpJŠEAQT@”Ÿ³‡Ŗā¦nŚų¦1ąāžX!D™„Y,œ<ĒÉÓ üڬ"3ZyóŌłPÖ(€fįŸM—ųīš!–3Ҭhł|iØ(čr¾gīĪóFzm Ƭ¢”rų`"ūøńB;+-Ō$&Ÿ¶ iŅĶąh«»ķń0E­_­7-×ĀѹŁ` įń&TÅń¦yžw&”œiīBB GlĆbŲŹõƒ(%æķB—²ü½B5±aS Mžóbx£8āŠ|¬éńÆh‹­I'Zŗ$ņ[ö±Ø*čtJžĶ~k&æ® akāĪ›}Üyr×%Ęż—Aī‘Ü]żœiā`CÓ^Õéž}īŃ)čŚų±Ś+œž¤ÜžB!„BQśE§Y9Æhh6V͊YU8 *„«:4Eaof&iZ¦ #ōŖŒÉΑƕ.qöØ'¦Ht(hŠ‚¢“ĪxBˆĀ) \ČØnīdrŽ¢Tø|`5ŽŽµq­ŌX CÜE*””éD·r£‚ X/grŃź†æDœ2aÉļūä¬ńœ·øŃ°Š †šĢėß_ŁyÕ«dD‘5<3:#õ+ȌÉäҵSļ*:œl!Ӝ5œµŸČ/ÜYĻș푵dķo†ģmučsa֑ß>ėmi\QOzt—Õ‚Ė%{ä kĀMʵ0ōF:·rÅ/3•Łg-XŌ›ē™|>‘=–ņ©EFĖę [#v‰ÉģŽ¹½:!DqYµ§ģ³ÅœŽbYŖ¼éÆ\wt¬„Ū:Šæƒ3É.ņsT&źęXŚ<ēÉŠz©|x̌¦™Ł{ĪDŸf^¼­°9ZW\³Ļ)®Žt«¤p!ĘL†ŽH ‡2¬$_sRP3-„åžX§#É ę43—RUéõ/„B!„ø‡©¤£#ÜŖ ÓfŃ„x»„?{Ćc©ębä&5Y³ė»/„ćh“¢ŅŻł2åµX@OĪČxš\= ! §ą! ó_ó’„}…(U–ŌH|żš–MŠæs©ŌøŽ~R6¢dæL¬VRģéŃĮo{šÉ„Ȗ¬‹acRVC÷ȶ0&„{ń|]/Žk©“…óA1ģx!„BqĖ:vė…³[9t:=ŠN‡N§»ņÆEQ²~Wt (Y+ŗ¬Ž«Š‚‚r„_YVŪ»¤Ūą'öl¤²Ÿß]-ÆSĒĀQ­Vž°Ķ¤¹• qvču:>jīCÓVžT­W{A‘Äī;‡žąql4ķ’ģŻw|užĒń×̶ōNOB ¤wDĮvŲ+6Ä®xzŠõŠg9żŁOE¶³+öHļMzļ-!=[f~ģ&ِN‚ļēƒ%[fgg>3;[Žūż~qcć0†Ó0° “€ąźśz™Š:Œāķæšl4;’īPīœā:؞™˱ęź+†ńś[o5˳iżzŚ÷R«ūTł³Ž`‹ßP»ß2Ķ’ƒ·m„~tbØ5°Čįf[~r÷¬#gĒ rv®Ą׀]+¾§Ūš÷BOOĖļǶ¼ųw-!ʝ€o÷ļ¬Ż¹’“—āŽJR!EDDDDDDDDDžP~2/ądz¶AIN›Ļņ (¹>>Ÿ#/Ol"÷Ī^Cra€¾Ž'®(—Ąŗōr{ń˜.vX ‹ü‡`‰<“ģߝų9·1Įõ Öībż‹ßņnDėwdį2lbœ;œ‰ŲŅ›v Ą*bŅņEthÅ÷›rYSä¢]³ƒ[ĖŻ›{ģažskYįYŹž‘8>źw¾Č+mi;čŃ9~ž²»¦gšsŪ ¾’2–°Ūфv—ÜČõ[Š,rģĮ0m~Zßž)Onć¢ē6„†}3ńœżw|ž3F/9ąå¶õÉ8ļ®ԚŒølv/ūĘĀO;+Φ=ś"žFN·AōhāĄ·i*_ūŒ™mÆęšS:Š6„ˆ}‹>füŲ/™±ĻŖM3ŗ]zWöoNšk›¦æĆŲ·f³¢ĄDu½Š—ö„{Z$®Ģ lśm¼»–L…ēǤŗ—m\U ĮV¾¶lÜNŹ~’Šz¶`å/Ļ…ƒ§wĀéņ`Ū6‚½ųņv‘Ņš‚œķL~ū~ś|9'j > vēŅ,¹™»÷é&`:Ų³m93·®&¦0½T¦&§°cÓjz5%ē —Ē×įDN5굕œĪŁüf\ȀĪ/ńÅ“ŅŲ6ÓéÜ1›_¹‹×ö$‘zįż<:ņÖŻ:‘_ Hæöqkõ />öĢĢO£ķy#¹ĖÜČļĄ’i‹Łw}/z¹Öń‹Ļ£!Żz$±śē…@łīœm‡ ĒCDųu¦§VŐvå“<Ńöc^|ā9-jMį#¹ėŸ>öŻłs½å‡>“Ķt:·ū’’޽“q™õÉøü<0¦7'~ł2/Ž~‰uĪ® y;÷]¾Œs^ÜF<-ÆĶØČ'yäžE¬rufš-wóšå·1tÜüÉpóķˆ’wĶÉ"?©%mÜ;Ų§š÷˜V—CąŠ»€uėl—¹*Ōņ×0(é÷9tŽP,rXäģZ/‘‰Į1Ž ÷āóyĄ†3"ų„õī#¹a{āŲ¾{.¹[——”Ž?{†åĆ0€ӗCdD{7ΧI'Ą""""""""""‡Ó1–Lv©O”åÅį ß9ˆLŽ„Bƒś]šiR°mŁ;³ˆJLą8Ć`ż² ü8'‡|GW6HęäöIųņņłé –&šöż»¼h³½&ß f-æ‰sśt vŚź°pŁĒöɟšÓ76ūXüŽ7ü|Roŗ„Ną§ķ'qīńۘśš;|·Ī,eŚ›_3õ„ćp,šĀŽ³éŪzæ,Ā—p<ÓaŹ<‹Šą‚N÷ņņĊ–µ”sŲ“9ŠV¦<8‘ÖGsųī…·hńü0.éł1s«hÜaŪ¦|ŹĻkƒė0ćߙ߿¾œĪā]n` ļ|y.Ć/źlĀ—|—õŸĻ¤Ūf°4ĖĢįÓ0ō¶ž0īcü‰MHwī`Žņ lĖuBī"fi÷žSØ«!°³ź›C£žƒ^lģāaąXĄvńąķ"rHķŁ8Ų``Ł G8"‚Įo(¶1Ą P˜¹Ž"+Š}Ų±üGöX:…ĆķĄ db˜&†aāšf³{ćlšt:C9ŒĪŠ °:߁šeé6IjՄö’œCf”E¬ĆGlÓdšwiĀŽõ{ČŪ›K·³{ŅīøĢ™¼ˆå[·Š,)š5{s >ဗ%ٟ{ä³ü "[Xq`ģX4™ļn<ž~-ü­¦0õåż֒kū&֘½éÜŚĒ—‹ƒŻ=[ī“kŗ‹õße‘½\[V±Ō!½»¹™ų£UįT…ģ[ś,żŠ‰Ÿ`ĢćgsZĆY¼²Õ£żUWĆ_ض 迎%Įph<`Ū( „ C!°üłTāŖéÖĻ]G£Mšåķ"3×ĻīĢ}ģŻ¾ž‚Ż+‰¶w’€· õ’‰NhDΦ¹ø"1#KąŽMLĒ“dwV=r÷l#×Įšõ›i’ädėRØ×¬WµĖŃńī<ķ Ž4œÖÓä®nČž…‘Ć>¦`ČXī<§1&Ų»†ł -8ŽqƒĒ#¢­Ū6 {łµŁõķHN¹ņS² w‘vaGŗwo‰‰%cNąžwsøš±Gx ŃJʍŗ“ē0wŅtļցo£ œ)­čÖ½;ī°łåo]ƼŠɱᄰyéč̌l@ģųšōgńx_OX;07Õ÷|3fŃŽlv¬ü™WG_JŸ wņĆōŃō‹* ™K굹žžłļ2ó²w}ČŲ;ĒšcÓ«ųūĖgŃ>Éb׊YĢ.4ˆ6Ūdφm$¾žO®æ½s'šŠšH›³˜ū`¾Ÿō„žH""uˆa˜¦Ž÷c%Ą"""""rø`²»ĄĄ••OēXÓ$*6šVńIĢ™³ ąńD’œœˆŪå!22ĖEĄvÓ¼k7>›4‹ sŃ“aķ›5įęŠr¢2ØĖvę<½ŒĀZ¦Ų[˜1}ž‹śŃ7b ?:Ŗœsß$ž7õBž»žĪ{īc~Ų™LŚ'ŅÓ±›)”i…ÓłuįÜ1Ģ eŻü5ĒyP5tīżŠ¦žĖ³×_ĒŁĻ}ĀO»ŃjčÕ Ķ}›æ«õśÄ'%ą6xķŚĒĶŽ¼łōū”Œ½ü®Žż ļ-Ī£(¹=cW1wmžøīœŌ-Ķ‹×±!£ex IDAT7†-ė“āŻĪ¬\‡vņcT]”ŖŲ “õÆ”Č7t@(ī:4”ibŠgšŁŲ¶]r¶‚… ».4—’VĒį3åą~"ņ‡õšząśį/ŻĄęŽÆČ _÷}QĆū4jęååKēåā«\‰[ū'uy©t²–.>¹÷&>©vńAcvMøŒ6JÆnŲ"Š©ÆŒf*@Tg’·½Ę©]^ ŻŚ…ų™’¢Ćq’ŚofѤĒĻäŗī½÷»Ž¤Iż4ØŸÜ8°‚eźL:ßróˆoKƊėBūG®ģSĖy5ģNrŃbĘ>¾8ģŹŸųf\ųD³(">£ ŽOn¤Ż'‡f?²m oaW-ŃsRDä02Œ`8‡ƒ¹sēāõz9oč0œ.7F(6†E1J‚E£dČĆ0JŽG†At\bÉš*FŲ©ųrš]·6äŠQņ^½ōUZįålKBa¾i:‚[ICڈˆˆˆˆČa°Ļ2ČöDd{ÉͷٱzŪwzٶ½Ģ .ŃK‹t~#†{²ńZL›¶”5 WŅlĒF.飐§›Ė×ųē nTš-O/vļw‹EĮ¬iĢ~"'txęVhŚŁ¬’w¹źz®øļ4†6°ü—Ÿ™²-¼‘Qæ’¶Œčū›³żæ Ųk\Œ½Õćļć”a×qŽįJĒ6χGü‰E¾ąņĪž“€óÆ}w[Žį§—Ąƒä³å­;øußõÜpŻs¼–lbe­cÕ'Ļ0wmvT2éż®ą²+ėÓ ŹKĮ–łüśĢ›|‘ķŌN~ Ŗėį/€qźŠvŁ. ķŠ?ģ`č s‹C\«ä²e[Įė­ŅėmŪ ]æ.ü–ĢBķżß°~ä•‹ˆŌ䯖eS”ŸĖņæ©""‡óˆkŪX–…ßļĒēóį÷ūé9š/x"£1M£4ȅ²nI°k’ša狇T1 LĆ ‚Ķ2Apńļ.Ėü³ų ½‚Ėł*»‰aš„õ>Ź÷æ ?ÆŁ”ūŒf[Vh `[–e…žČÉŚĶ/“>Ņ. """"ģÄ!›ü1„ibšfčÆ#ō™Ē¬ąóMčsŅ~Ÿi÷ūše3'‘֬٭—kõVęF揄m‡$vģŹ%µy2m{e0ł‡lY³“mc:=ģÉ$ÕS@ŸfQtN UF:ń)ÉÄŌKĀ‹•ŌŒ“®x¢Nģ'ŽĘ×ńÄóo~–’Š”Mė×Ó¾÷ZŻĒY՗"Å_0ĮÆ‚éŠõĮQ€MlŪ }ebca`‚i'°Ķ`÷Š”Ų0Bm°ŠČĀaIoxęk(©ąl S_ž‹ˆnÅŻ?;NLÓÄķvćt:p:]Įn”÷kŻ[®åoŲ—å_³46(—Ī×ūžDšĮmĢąeĆz‘Co‘7‹Dņ}~–,ÜIR‚‡¦É,ūx>©Y^ŗ%“4wŅ<-š“žõČčԌøś pDFćj˜m0Š °£’ČĻ)¬#kmŪ³']W½Č³9 EŽ„ŖŸyūu]œŠ–~=b§ĀŲ€ Į$xÆ`+_ہƒæ^/¾WqŃeē‹ś‚© ŪÓĘ45քˆČĮ(é6ŲĶmLӉĆéÄa”÷Ą„lxx[¦5oXŽ ø8Ę0ö»/%Ż?+>ōŪSDDDDDäp²m76ųŒ \,%­e ±k¶qQŗIć~14IO”Az›¶ÄÓ°9Fl2ÓĶ‚5ėYüŻ4z¶J§iżD"żąóŌu6›ŃÆ_=6ž¼€Ż¶>w‰ N~ńQŚ„X0õ5ģ°X6ģ²a„g³eG+nŒaƒe…&¶Ą„Ē”™Fi7ŠūēR²:&ˆˆŌä$†mƒ„?k7³>ų€eé3¼_2¦öłƒ„¼%Ż&˜fq'faŻ5—v5 [śV2ęo™@ø\ė_u’,"""""RYf2Ų°öąŒŒĮtXtKw‘‘ę"©q ɍ[Óø1ŽčdVmŹdEęF–nĖ$?`Pąõ±kżNNØ×kW&-c£ėÄ:ūÄ)é3˜>ׇ…[;ČPMŪūššwæVĄ”ļš‚”oŁ»››”īĶ°Væ„-~‹».£“A±ˆˆŌ„mƒma˜Ęx “'Ndžłg0¼_ņ~uÉgĖāłlŠéBļŃśM‘ˆ6†>†¬YڵpÉßżZW06šž×‡ś¦²æ""""""R—>8ąväpĀ€N,Yø†Å‹všĪ:5‰$9%›å{_“ŒĶYn² ‰kE·ŽŅ[xhÕŗ©MR±\Qątšėę}ub•Ż[^åžK…æ"GL%pe­€ ģāŲ® 6 lŪ }UUÜā×.8¬ č’ ʼnrX’¬ŽŸEDj(4®ś‘ol“óūg<÷Āūü“x™> [Šå/×s’u}Hł£ĻæŒ7ī½——M¤—`9ŒL#ŲŲ4%oŽKZåū1[ųŪį·UÖź—Ņ6Å ‚EDDDDDź Žm€ĮĪKØß ˆc5°: pQ6 ¢ŠhĄ¶Ģž¦«|"r€Ją²Ż@‡ ‹[īåBąā¦æ&6vč¢ģī¹xĢ_lŠĻŪ„Ao(Ÿ“ˆˆŌ€ü„ŽiŁ#§ł#ß6†é Ļäś{o¢EœEę†e,5cˆUßĢ"r vķµ6JCąŅ_;–¶ģ uć\®kērį/•vł¼’c‹ˆˆˆˆˆˆˆˆģÆź.  J».žß00l»’ø“!Æģš™²żFŪać‡\®'hµ©‘P `ŽpŠžÕó˜—Ó€³ĘŒbx׊KK’9«ĢDŪ™ņŚs¼ņĶ\Vķ Ōćv.jƒaķ䇧ęÕßÖ°yGDQæĶ®øżN.ė|½šn仗žę„Æē±)/‚&[įÉWåKÅĀg/ ė³nNż=OžÖnfLx†ē?žĘʽÉ­ś1ōęŪøŖgŠĘ‘Z3LGذö¦ø“ h(;fpčL08 xˆæFų(æjų+""""""""µRE\ÜæĮ†½v¹ö 84mqІaēTÜtx·ĻPī -}æ%"RCvš‡5ęīŚŃ(TĒn¦3mŅØÜšĢa$w}׈«ļxœūźg3ćõ'xü®ēhüĮż ˆĢaͼģi9‚Gļk‹»`“ß|‘'ļuÓģĆūé™Ėō§oēžo¢9kăŒjįdēĀ/·Ä®"vŅfŲ9³&&1 =@‹ĒŽäÖ÷ †Üü #[Ų¬žźežæć _Ē_Ūk\©`÷Ķ”ńͰֿ”÷Äįļ+j\— ż‚`½K‘š)—ļŗ¤ pÉEl#,.Ó 7|¢š™†.†\ņĶW濌jż+"R%݉AfźŚ@ėkŽā”Ķ0ž]ÓČ[3œ oOeųčĮÄh—‘ŚƒM3x*ī:ü=0ūu]¦ÅońūfŹv]ņ¼ģ[ņżĆ_u’,""""""""•)×ø²ø¤+h#ŲÖ7žĒfæŒ7t6ōŚū?@éĒÅvłŪED¤ZvØ hĆ<Ņ»i~Ęߙxҵ,üå>ūģ-nś®Ķ“×t&jĆJVä³åį³čõHń},ü>Ļī¼ ;ž7§ŃÄŲGV¶E`ÓjÖł0ųø†ÕMs`ĆrVÖgP·ŅPG=ŗÖcģoæ³!0˜ķW"Rs†ģ…”¤ čr=۔Ƿ|š[ü~»lX\¶ėg…æ""""""""RsΚM¶_ ”ĖĮėķP \QŗHŁģlĀūŽ6ŌˆČ±‹G[?JĀ#¢!]žr]žr1—¼1’«_ĆŪǿŠf2§’ėY®mž°šD„Äc°·ü¼X¬°µ=č^"ŌĖ„ˆāć^h<_ĢšŽöl÷»®\š[z>ģüWDDDDDDDDD…płVĄCłÖĄ%Apń“%ķzƒ]F—›Wxąõž,"rlƒ£/ ˆ$£OWŒ}[8ŗdŠĀż>+7Z¤ž„ełq{­ŖēęhŚŽÖž˜5s=¾NUŒū[ü2ć!"rrʶ.ĪēCęĶŪ‚Õ©i°p`sģ"¢U[šŖõƈŌRq\Wt<6ĀBŻ2]B—½\ęg‘•„æjż+""""""""Õ©“pU!0’ E»įApØuoIlšFx««ā`øÜcŖe–ˆHķG~ `ļāwxč‹:÷hCjbÖ¾õLy’SÖGtā²6NŒ„vöžśÖß¹Ļq5ētn€»pkö„rĪ靈®nćrÕŠę\÷Ę(īį:.ģŚwĮ"ÖTrG:ķ2ܼūķ&v¼€Vö62rZ§ć¹ņ¢¦\÷ś?x(źĪl«¾ĖųµĶ¹üž”ń-v|żw®xrg>õ·vvV¾y7¼ćįĘWžåҦ&Ē“'®bŌoyšæ38A”ŒČŸól†Œ Žåż”źą·’÷Ź EDDDDDDD¤ŖģŗŅ* ‚Ʀ²Ć'·Kn“©¤ZIP,""5üŃĶ‘ l|īxā2āĶ'ßfkVfT2M۟Ą]όą‚F&MĻŪžć™„’2ö‹§øóÕ<ˆi@ė“nę¤!ÕĄą”ӍĻņbü ¼ųŃ3Ü>.ĖOƒ“. nSžuÅˆē”æŻĶܿċ£~ĮF’;pj§ę7āižx–ē'<ĄM™ÜŖ×>5’«;x*xa æh—’™’Ķ!č–ZDź2Ć`æĄeŽ1—æ“_Ą[“ą·ų}¹ˆˆˆˆˆˆˆˆHM§Qķ7ו¹mWsÖ®tņŽ ""ÕŸmĖ"/'“?|¢‚ˆˆüĮśœ|ѱI¦Y>¤-’ė”ņWUŽ!x­Ā_)ó™Ģż³ƒ?N²Į¶­ąķ¶e[ŲVš²e°- ˲Bädķę—I© """"rĄNr!± )˜¦Ć41M3ō×üq¬ib†zI žXÖ ¶ż2Œ`’GaCŽīĻ;ĖfN"­Y3m4©Ó6­_OūŽCjugM&*>WŚšü†Ÿ-mõ»’ä„*m,""5;H«‘#z ®śXlTy…Qķ{p‘ŚpÖf⊻„.¹5ģ¬]ń-v5÷‘`čX*"rĮ”ß°WōÖīżÆĀ_9PfmļPŃgLUńÉ?”“N:é¤Ó”:‰ˆČ‘Sę˜ö~·–횽Ļ©{/ >‹ēdŲĄKö.~{ć žż~+Ö1YŸ#¼~»}jżxŁ,łśMŽśmū±¹=l?S§ģäé%¾Ć»Žv€łó3y]ąŌ¬&ūė½O‰ˆˆˆČQĖ<˜;ܗTJltŅI'”‹ˆŌuw,Vč+rl°óךÅ3£øāā rī%\ńģ ņކ ģ`ĘgŸ3y}~…aPųrŸvÖłüõ½ųÖ}Äm—ßČ3³ó±­}¬˜9“%; ŽĶ0éHÆ_UŪĒæœ×o¹‚kĒ-”ØÖ;dŪŸĆ¼ūĶ×ĪdĮ7_šóźœc7“,_“Ļā}öį]GŪĒōyūųu·UėĒ9ąćEMö×jžó""""ņēįm’˜هu¹sĻ惃gŚåŽ3³ī{Œ³ŽŽˆÜ|‹@£Ę ˆÄ°Ų“UÄĢ%ūxąķ\† iČmŽƒėvīXTW""""Rē车ˆČ1bėÖ­*‚ˆˆˆć\¤“č@ 3\°±­:v¤µ°3™żĪc<4u9ėwdću%ŃÄćÜ?8-“ēw°=Ē‹3.•Χ]É­—u'Åģ=LyõY&ĪŽČ¶=ŁAJ‹žœŻ œŪ6Ć.`ĶwćyįĆé¬ŲUˆ3¾1}Æü'£NŖa{)ņŲśÉ(N’ĄA›«žēŁ ›`św2óƒ7x÷ēŬŪc‘Ń›‹®æŽ3[EaŲ™Ģ~ēeŽŁYĢ©čśĮIUĶĄ»•_ßĒŪ?/aK¾‡†mšćεqUTŹJ–ūéćērψI’×8FWĮżŖ\§*źTf&›«Ü•Ō¦&5(Yæ=ü6žy&ĪZĻę]Łx͵ķĶŁĆ®ąģvq•“n®f¹¬L~ņ:Æ}=‡5{D¦dpī’ęņv®ŖkSŪķcmąķ‘wńS÷Ѽze+Õķ£å7ĖĘßČićÜ?źmžŁ?øŽ»~škæŻĶĪB'ÉūĻ£¦õ 1#]thA$@zƒ:ĘŅķ«­<ńżn:6jĄ)р`ī¬=Œ_TĄš|HŖÅŁĒ'qqš›9?lāž­±¼8,‘ šä«;ŲŅ7';[¢ļ[ŗƒK~6øēŗ$ÓwóĪ:/[s- 1H©Ņ'&s~#³āķZåćm^““L+`[”3ŅE—ŽIÜŽ/’”āś}üņŪ&ü^ČÆI£Fn܅„m;‹Õ‹÷ņü¬<–ēŲø"]ō=¾÷·wī·_Vq¼°ö2ļ£×x}Ņ\ÖfARóœqå5 ķœXy^“}ź@ŽCƒ“Ō€ˆˆˆH§XDäŃøqcADDDžÜģlV̚ĶöʗrĻM퉵r1'``Ųī4®½ė\R¢mv/ś”—Žz†W›åŽžŃv.–,#³éåÜ{K®ĀmĢüߛ¼ņŲ뤾t Żw|Ī’CŹe#łænIŲY[Č«—˜4<õvž}N:&žÄ˜²ä͇xdr}†^·$ē2÷ƒ—yé?ÆŃšÅ[čQɲڛ*Y‡ję™ĻÜq2ę×hNv7„;Ų³ģG&®Ø$`¬l¹ Ŗ*pÕĖŠ}guu*VŻöČ>°„÷£kē²nŃv§_ĘØ›Zā.ŚÉĀIļóŹ?ב7ęQ.kYQUŖY.¼¬˜ų0’üŌĻ€ĖnįŹV1ųöfÓŠYmmlū”]ŸŖöў‘åæīiyž}Ü}R &&ŃõÜ%·xŅś1üģN$»™łŃaóØE}+Ż„œœÜ/ŽßŹbŅ*?'wq°|Śvž1ØĒ ɰnY&ć?ŁNŃŠĘ\ÕŠ mZīeE¬(„Œ(ģ-ä÷B›]ۊšvŽĀƒĶŹ­EŠ0Nn‹Æ7²7%‘ūOöąöł˜1{/cæ4H»2…^å¾å²ł½šĒHlĖõ§Ē‘āŻ÷ńĀ“]Œ­ŸŹß[™XĢłu£—œÖÆ>·$Ćī­¹¼³­4¶vgóųOł¤ō­ĒÓĶœŲł^ņb+ėF½¢ć…—åo?Ä_Ą‰WŽĪué6ė~z—×zļc1¼•»‚}¢&ūŌ‡ōj""""Rē)‘cˆITÓĪōīÜŖĢŲ©ŃĶŗŅ/t¾uĖÖO¹ƒļVl&ŠæM胱AdZGztn…ƒNtNŽÉ¼»~aöš]­,öŁ1tėŲ‰v-#€–ū=¦3¾!M›6-yL;ē7ž÷Ķŗß4šażƒ-N3žŗ“9׿Ļäe7ҳ[%ĖjU|½3³Źłõh=“Ļ~ŚKŪa2ņ¬†Į§C k~˜ĖŅJkUĮrWW· ]ŻÕÕ©ōq«ßµÆAĻ®r•Ö‰^]ƒó莭Ü>ŠO>Ēywö&²¶Ė•7‹¾ŲH³‹ŸįĪóRĖģ_‡gū”_¾ŹöўĖģIlBó¦ Küо›Ń›zkŅ1q³ļ Ī£GÓŚÖ·’g`‚‡ ĢŽėĒ_XÄ |“评;»ø0.M\äļŽĀsó¹čŒh¢›DŅŽ½,ÜjqF†Éž-…lw›°µUVń²`‹E³ö‘$6QIōJ÷ą ‚.1~ę¼—ĖĢ6½öūM¬]˜_żćŃ)‘ōݧM}ėVnįŪm>­<8 ņłd™ŸvżR¹£kØEoŖÉš„ł,).mA€}˜tO¤}}pW¹Ė=ļr§ņįW›i~ń3Ü~f*&йCc 6Ü·Ļį‚Qżˆ®ąłXŻ>Uķs¦[åĒL©Ū‹ˆˆˆˆˆČ1ĪǶ0žÓX¶y/E®X\m}•ŽĆlЈfūr,œ=žĀŠ.3x執²ņ„Ó8ėō“éß2¾Ź°$°eė Ųńģ5œł\ńµæ‰go>v-× ŗłł·®g£?…žķź¶Ö{Õ-ƒcPMėTūķQ³šĘW½īīōč”ČG‹V³%Š›ŒZ.—ÓjV¦ŠÆc£rėt$¶Oų>ŹFwfżŅyt}+Śf™E¬ń;闼,ķĘŲtŅ9ÕĮ„µEl¶¢iIļś6mōāĖp³h£¶Żˆ˜›Ė‚½ŠŽQČĀl½š;1)揘ńNöąć›6ŪÖdńŹģ<–ķ Pč4q{ĮŃ(ų, dyŁh9ŠŲQéś;ĒrIz/żo3+ŚĘrNēXŌwŌx«6ÆfmQpß*YNGcŽėĢ[sV³9Š6ūß§ūŌ”>‰ˆˆˆHŻ”XDDDDDDŽiֆOłĻć_bžvwߐA¢±•Æžž?¦UqĆtąĄĀ²WSĪ}ą%z-ų‰Ļ?żœ'o’„OÆų7^”§Ņ9Ų`&1xä\’™D&ĘbU˵Øf~›Œą4‡5Ń©f\ń5ŖÓlšÕ“z†iRY‘Ŗ_.»ŠņžńŪ§Ģ>ZīĘ™ĒĮץŹ,bU4Irā «l8éŁĀĶø„ł¬*“˜½ĶIĻ㣉ڔÅ}œlę³66’[’+^Ć0p–]閩zywļ㔯²1;%3j›D||ńķ®rūc•óqø9ļ¼TzmČåÓ¹Ł<žĪ>>īߐĒ{yŖ8FŌn9+ŽČÕķS‡ś8$""Rs>ŸOEųp¹\*ĀQJ°ˆˆˆˆˆˆÓ¼ėW³ŽnĻ­— ¦k¬vMbĶŚĶĈ q×ÓŃu0Ēæ“»æųžeēeе’Ł87£©ó ÖlµhtbÓņ¾­Ś=|uó³S[ŅŅóóēoĀß¶Ł”ż°oÕpŖŖ“óą·GæŹõŲĘŅß÷āIoF#(»~Õ-—£q3š¹¾bń’mŚ–ķśˆnŸrÜxܐ—›GmŚt}>¾›–ĶZO$÷“rātxhéĢfńf?V£`ĢX~m QĻCjؼiQ4Ÿ‘ĖóĢŠä¢x'ŃĶŻ¼¼:—ÆL/õ3’ČØ<»/§8Ŗw$V’ųŽŻE¬³#Ł7†n€m’V—Š<ęošįo䮢.&MšĘqsÓNųu+w,ČaiwŻj°©-i鳒ÅK¶cµm\ĪĄV/ŻCDó–¤V°æ:j°OźćˆˆHm(9²‹ˆˆˆˆˆČ1͕ڔ&öW|żž/¤ŸN¼c/ŪójŽŽĪŚ6›ÆŪ4kžB„o7 7åAl±U4‰4āzsž©q’’žąQó"Nk_Wįn6ä6äŌAm‰Ŗå:T;明\tfwų’įNļXwĮr¶D“S3–„X›‹~cĪÖFōj\õ2DlÆYt{T[ƒrŪ#Ąī™Ÿ2±ńń“KMæ¾ĒÄu ųĖ5=‚ć©ī·~Ż«Y.#®ēŸö÷½÷ŪC9¹m޼6čOæęG`ūTĘфŒę.>żõ|ÖfĶķģ‹ļĶ ­u}ĮŹ÷±hS!nĖ"3³ˆKsųyƃӇ¤pR4D1“‹‹;gīä)W"''Ćŗe™¼»ŪŃ£Jʵ5£T/‹q³ż¤õhLsĢ–Q4’-“÷m— t×ģ ,ĆABģؘĒģö.z'V’ų$7©dóÕĢ\źµvośŁ^6ˈ(.īāāŽY;yˆĪlāÄķ+d³7¬Ył|±š§8‰°ü,ŲkA„£ŹcD™ÅŽéŅg¤rĻOšLÄåœŌŌfŻļ0qcēßŌ³ĀżµWćź÷©9ŁŁ3xźöēYÖūž{Cē ĘŹ‘ŗ@°ˆˆˆˆˆˆÓ-Ļēž›öņāGćł×ēyXĪb›Š¦Qtŗµõg­gźĒ_šŹ¶l|ŽfōbÄČóh頊tQt¾ęAž7·ĻĆó!*…ę†3`µ€«ŸŸ›¶—’›Ń±o2įėń<ō^.W,õšt Zԁ;kŌgš%ē0ż…ļxõŪ^tæŗm•ĖପN‡d{Ō¶¦ngó?ų/wy‰i҉³ī¹ž+;ET²~Õ-W$Ē]õ ƾĮ›ßŒåĮ·‹pħrü5éŪ<łß>•n·X^5‚ÅOæÅ›£gˆjDa­Ųś`÷±²µŽ4qlĢāž³0& ±.Ś6ē”Óćč›h”L×¾_CqīaÜģ]|Ÿ‰õ"¹ģÜd.i¶Ö†‹bļ g IDATĮ"xcG€­ŻĮVĖńŃœŲ “•V §Ö«i’źää>ńLū1›—GÓc §ŚĒwŌēŽĮžŸ½‡æĻ³°&±Ń.Ś&˜”ķbŠ®_CĘDdņś¢½<0#@Ąį ^bĒ'§ńå{™2;›±ūųL“F ¢ųė©ńĮVĖ5ā”Ż°ņ ē5^’č)ī߉ͻséæ®ehkw„ĻĒź÷©;ŁUvw.""""uqźŠzO'"RGٶmYäfg2ż»āĶgĖāłlŠéBļчö .‘#Øļ©—ˆaš†Žnrx_óCēB’l°mllŪ ŽnŪX¶…m/[VŪ²°,+ō7@NÖn~™ō‘ *Ē>ko¼‹ŸŗęÕ+[Õød©Ž‰C.$6!Ót`˜&¦i†ž:0 #xŽ0Į0‚— Ć x#4V}čg6‡łsŌ²™“HkÖLMDź“Mė×Ó¾÷ZŻG-€EDä°Éłż3ž{į}~Z¼‰LŸ‹„†-čņ—ė¹’ŗ>¤–ńƽ÷²ā²‰ōR,""""""""""rŲ(‘ƒfgžČĆ·azĆ3¹žŽ›hg‘¹aKĶbM  ‰ˆˆˆˆ+?µNEØ”Ÿīh®"ˆˆˆˆČN°ˆˆ4’źyĢĖiĄYcF1¼kč„„’‰œUv*>{]Ÿpsņčļyņ7>¾ŸŪ^žÉ–}^œ Méyī-üć†~Ō7kS_}‚WXĢŖ­Yx]) ŗ{?‰ś§“$ɱ“­¹Ŗ§ˆˆˆˆˆˆˆˆˆˆČR,""ÉĘēŽ'.ó'Ž|ņm¶faF%Ó“ż ÜõĢ.hdńœņ·»™ūļ—xqŌ/ų£ÓčcN½š žsĻn›š4·½—KĄE|r:ScQ;`‘Ś3N:ĀVDDź&Ū¶±-‹ÜģL¦÷” ""ņė{źEÄÄ%b˜&†”Ÿ®Čį}Ķ ż³Į¶±m°m+x»mcٶ¼lYl˲¬Šß9Y»łeŅG*؈ˆˆˆ°‡\HlB ¦éĄ0MLÓ żu`Fš¼a‚a/&†Aš2Į_ż??īĻQĖfN"­Y3m4©Ó6­_OūŽCju,"""""""""""""rŒP,"""""""""""""rŒP,"""""""""""""rŒP,"""""""""""""rŒP,"""""""""""""rŒP,"""""""""""""rŒpŖ""dž­[·Ŗ"""""""""r¶mć÷ūń{½, +Ą²,lŪĄ0 LÓÄt8p88].œN'†aØxŖ­j{ŒP,"rŒhÜø±Š """"""""ņ'åółšāółp:8].<.¦Ćiš%A™mŪŲ–E  PXP@ Ąåtāöxp¹Ż*¦j«ŚÖq €EDDDDDDDDDDDź(Æ×Ka~>‡Ø˜˜*[E†įp`:ø€ˆČHlŪĘėõRŸOaA‘‘ ŌT[Õ¶S,"""""""""""RĒXyyyŲ–Edt4.—ė€ēe„B8ĒƒĻė„  €ĀĀB¢££1ÕVµUmėĄ""""""""""""uˆ·Øˆ‚ü|<‘‘x""8”£ ŗÜn\n7EädgūOŌŖRµUm €EDDDDDDDDDDDźˆ‚ü||^/1qq8c+GOd$—‹¼Ü\>‘ŃŃŖ­j«ŚÖ¦•""""""""""""Gæü¼<ü>1ńń‡5D+ęt:‰‹Ēļ÷“Ÿ—§ŚŖ¶Ŗm”XDDDDŽMv«¦Nāū„YŲŖ†ˆˆˆˆˆˆŌqłłųbāā0 ć{\Ć0ˆ‰‹#ą÷SXP ŚŖ¶Ŗm XDDžÜģ|¶,śkóŹDžÅ<éiœūĢ<ŠT%‘£„—}ŪÖ±a·ĘĻļk×°%Ū«XDDDDDDź4_Q>Æ—Ų˜Œ?0D+f1±±įõzU[ÕVµ=Źi `9xö>¾ū×åüóūŻY`ŗ"IhМŽ}’Āš«. G=ĒŃ»ģže¼qャøl"½ZDSņ6Ȧ~zSšÕŃÆ„DžŠO{Y5w6óVnbē¾Ž(¤Ń¦{?z§e2ū«ÆŲÕķ Ņ“Żś ł,śä5~vžĀ_ĻjƒK[CDDDDDDŽV @^~>±±±ę‘ū¦Ź0MbbcÉĶÉĮįpü!]łŖ¶Ŗ­j{`‹ˆČ! ;kNךŅßśéĖcϦ|žę1}=ĻMø›~±FŻZ%³y…‹µqEž8E[˜śńĢŹŠ!£SNn‡ĖŸĻžm›(ō›‡4šõīYĒŖÜDŚ6MĄ”Ź‹ˆˆˆˆˆČQ,//ˆČHĪ#é8"<ņóņˆ‹SmU[Õö(„XDD3”)ėD@÷> léć‚ė?ęÓY£ß |¦¾śÆž°˜U[³šŗRt÷xƜ‘‚iķfʄgxžći¬ÜkÜŖCo¾«z¦[ßZ»ųé¹G7yėwdQdʑÖé.q—PŚB׿)Æ=Ē+ßĢeÕĪIķNąŖ;nē¢v1֞Š’T? Ÿ½€®Ļø9yō÷U5;ŸŸ=Ć£oüÄŅķ…øÓ8įę§ųĻ™Ō‚X¤Fül™ń³3“éwį¹ō©_Śž6£MĒą™ĄĄbėä7xjrš­l«ÓoäģV^ÖĶų™«¶±{_!Gƒ.ęŒ6•=–Mž†Łüø±#-š&©ā‹ˆˆˆˆˆČQŹ[T„mYx""ŽšerGFRäõāózq¹ŻŖ­j«Ś…‹ˆČa㈌"ĀšSä Y,<•Ķi×óŸ{:ČĮŃ4 “"É­ļ ¹łAF¶°YżÕĖ<Ǿ<Žæ¶w٬š=-n`ōضx ·2ėÆńŌßV‘óŹXnhć ˜’ĀHīś®Wßń8÷ÕĻfĘėOšų]ĻŃųƒūYŁćÆœ“6†GĪl€‰ILCOkSõüūm›Č?Ÿųś×’›ń}ėaķŻ@nƒd…æ"5åßÄāåŁDµ9‘īõ«ź|٤~÷3łKūX <±N ‹ķk×±/”C5&Ā*ÄHŒĀ Wu‘:­° €Ččhަ¾õ 2*ŠĀüü:¤©¶Ŗķ±L°ˆˆ:–Æ×‹éĶeēśy|žāG¬ņtį¢.Q%/³1-{2°gū’.WķģÉLųh­Æy‹†6ĆzvM#oĶp&¼=•į£š6¦ywŽļ¼oß¾­0†_Ļ;ļĢąņ‡•ż+o~²‹~£ĘrćI @ŪQŪųķüń|æą.ō­ųńń’D$§“Ń2µ4°µŹ®š]Ķüūxö’eĒŅ·[wŽk “Ńž R vī^öz RŌ«vģ]gt")Éń„"ģąóŪ“œF‹“•>EDDDDDDźŸĻ†Ėå:ź–ĶårQu¶5„j«Śė!°`9dŠ&?Ä ż ^0\$“ĄMcīā‚Ff„AL`ĆrVÖgP·°šÕ‘F®õūŪļl ¦CE?s·¦_÷dޜó;iµa%« ņŁņšYōz¤x" æĻij;/˜„@5ów 9Ÿ«{žŹ“·\ĘŅÓĪeč…gsR›D-*RS%OŅĆ÷ŪPkŪ^’pY%Ē£m¼ųĢ÷€I½>—2¬O”¶ƒˆˆˆˆˆˆ5¼EEx<žŗoėV­økämlß±÷?śK—ż^įt‡ƒæŻ4‚„æ’Ī÷?žT«ĒšxšhÖ0 °Ćīo&sśæžåڶᱫITJ<{~«šæ;‘KŸłž³¾āżw&ņĄšwx÷¦gyłŖvx“kˆT’|ŽI Ži³u×ü¤–7©f½nœwi[üŲäžž_īhÉy'¶Ąƒ3*“"m9*Ų¶Ļė%*:ś€īærÕ*>łüsn¾ńŗvīĢ?|˜‹•›īž{ī¦Oƞœ4čDLĆäŪ~Øńcø= ņó’tµ-ęr¹øęŹį x<±±±äääšóƓyķĶ·‚-aП½¶QQĮéēēēÓ¶MiO‹‘‘‘˜†AŽAŌ¦®Ö¶¶44”ˆˆŗ•øT:tģ@‡ÖĶI­Qų ަķhķŁÉ¼y[J 61wĮ."Zµ„ieMh­Ķ,\“OĖ ŅąHĖ …;“•-R›5£yÉ)1U,‰į!"rrŖn%\£ł‘¤÷¾»Ÿ{›—.KdÉŸ³Ą§żB¤fŸšRiŪ2ŠÜsXœØäłźÄå„¢Ā¢Śżt¤xbg4IõźQæ^=’bœąŠ%„^=ź×K!)ZķõEDDDDDäčį÷ūq:FķzŹjŻŖUÉłIß~Ēē_~…iš\9ģ²rÓŗŻnz÷ģüČmXvķj†Ćį8Ø ³.Õv×\9œ³Ļ8½$üˆåģ3ĻąšįWŌ¼’̵ķß·/o¼2–ēŸüær·=÷ä’ńś+cé߷ϟ®¶µ„XDDŽ(#īx®¼Ø)+_’}8…9s§0ń?’`üŚę\:l@Éųæ`±ć×w÷ł/LŸł ļ>ņOĘ­jĢ9ö'0N`ŲŁ©¬{ėļÜ7~SęĢcęŌI¼ūÕbņŖLvÓi—įfć·˜8e.3'É7‹³Ė…KÕĶߌ<•÷?›ĀÜe+X¶p³×ę@\ńz„©!-ś ¤­g3æ|ų?¾µŒUė7²~ķJĪü…ÉĖ÷a›‰ŌOq’¹b ÖnfćŚe,ßVXÅ&’ČČŽ“ŠõY^•XDDDDDDź æ×‹óĘP½ūö‘œ~Ś©˜†AƆ HOO/7mn]ÉĻĻgĒĪ¼ōŹ«|’ćµ~<—Ūæ®…”XŪż8šxFŽ=Š3Ī»€3Ī»€‘w ŽvĀĄƒž’Ÿ±¶śõ掻ī ::š„æ—ļ¶|ÅŹ•DGGsļ]wT\k[[źZDDްŽń’ģŻw`ÕĒńļĢ–ōB jé ]Š€‚‚€å)‚ °+ØĻŽ"¢OQ,ØŲ{‘¢"‚¢t¤# MZH/›Ż™÷G6 R!@Źļó^ČĪNĶŁuöΜ½ēNąåĄ—yuźhn= ՚tēĘļį†V¹‹'øāłcŹÓ¼³×CXƒŽ\öōHnėäŸBē»_į„ȉLšń"£ŽN†Šš4=ēvĪІ‹Žō½ó~–?ńÆ’w>ސzœ9¼żZ¹`įŪwĘnį§÷?ęÅ]ńxœaŌmѓūæšfźT(RlFhś_Bķ„ĖY³v!“=XŽ@B«Ö¤Q[64ķћ]sć·™[±Ü‘4ģV‹fµ Ś`8-ŗ“gė¼µ,Xۈ˜Ńžo?DvøŒ»:(ę""""""R6ł,‹€cH¤ķŁ»‡Ū†ßBƒśõ©Q£gtź@bBf®^¾Ū·ē–‡qßCšĻĪĒ|œ‡Ozz„ˆķ‘Āż=7oŁ’ż\Öćš\½‚Ūā‰ŒˆąīŪoÅ4M>łü ޒ𣣖yįåW8pš —]:ˆ»ļøµėÖŸP)b[RFæĖF؈ˆH¹dŪ6¶e‘”p˜ßē~Vq’Pk+o½žļ»æÉ—··D9U)+ŗõBhx Ó<īŅQ"E}ęūł’oƒmcŪ`ŪVę|ŪƲ-l+sڲ|Ų–…eYžß>ć2Öē Øˆˆˆˆ³³ &,²:¦éĄ0MLÓō’v`FęcĆĆȜ6L ƒĢi 0Ą’Ļ æŽZ·xõbbŹeœć&4<‡£dwĀZ6oĪ’ž~ Ó<ŗ,Żģ¹?šź“芮wŻ~+£Ÿzš’üs\ĒéózINJ"<2²ĀĒöH3æś€ó/¹“XĻ+¶…zՕ\1d0«×¬åįŃOd_OĆ07ęINo݊?ł”>ž¤ĀĒvēöķ“ģ2 DėØ0„ˆˆˆˆˆˆHAR×ņŽØk¹ųś'łz‡Oń‘“¶ķ|“øEY·a<ń$ė6l %%…’Ż€eYlܼ…ömŪr×ķ·ņÄŲqĒü0M³Äc—×Ųžl•-¶]:wą‹ÆæÉõ%č̲Ļė7lȳ/¾ž:Ļ:•!¶%„Š""""""R~X{˜1v4Óžń$-š7ē–‡a;u",4”»v1eś,[¾¢Äū°3¶ŅÅödØl±Ż½g cbhŲ°!+W­*tن °wļæ•&¶%æb)϶58ż‚»øw@MLŪĒī YļHeõ”Ń<:u>ė;ØZ=ˆŌ]«ųęå'yqįal#œŽ]Zą6lāÖüÉß>Ą>̟«žĮ‡IćĪˆ:źž@>Ķżg²ōŸx¬ĄPŅ’d„ā*j€ū3Æ>÷‹žŽĒUŸśÕŻ$ŹĄ¢äƈˆˆˆäjćš&–eūś†Į]wÜĪ€óśˆĖåbóÖæ‰®U+;9W%2§ÓIƘF?ü§·nUāżŲ>f9K¤olO–ŹŪ%Ė–pÉE¹ģ ‹/`ńŅ„•&¶%~-t‘ņĻM³ÖM0ĄŠŪĖŽūą/|ųżN¼®ęÜšŅ»L{{2ļÜŽ`āX4ūwŁU:u„•ĖĄ÷ļ –ī“°ždéĘ lg ŻĪØuŌE³»€i_o%ŗŒx…Ļ>šĘēŸLcüÅõ0ŠÜX‡ö²×cc“įśg^൉ońé[·ŠNõ¹DDDD$ÓįĄēóŪŗ†Į· oŸŽŁĻĶų~oM~`Yļ½?—&¾ŽĻēcČ„ƒJ¼/˲p£doE‰ķÉTŁb;cę÷¤¦¦Ņ±}{®ŗü²—zՕ“oŪ–””fĢü¾Ņͤt‰)"RŽh,:‘Sw ÖIX¤,É=~™Žæ7²ÉccŪ˜|Ū&ēZŌ<š/ū}P½jĪjó+—’Ćāe{ø0j kŅĮ٬'½ź˜pĽ ļ–õlH·1"Īą¢~õ4GĮČų³ū«ß‘.µæaēĪU¼:|8?Ł—K.½ž ‚õś‰ˆˆˆH6‡Ću ‰4Ć0qóMō;÷Üģēęüš#Sޟīo2Ū„‡‡šå×ßšł—_pčŠ!nŗįŗļĻėõb–³DڱĘöd«l±=Ē+ÆæĮ#ļåź+.§e‹ę|žå×lݶ €Ę 2xŠÅ“oŪ˲xåõIÄÅĒWšŲ–”Ą""åžQįĒ+)»§`e€Eʊž\¾žtĢjuØd€ma†»!½žÓ‰:¹ŗóa-‰rF$ŻĪ:7W,eėļ?ó]ÕÕ¤ą¢uÆīŌ69*œµM00üēŗ?gKn|võæü‚Æ~\Ęŗy²nŃb®yę māŅĖ(""""8].ŅSS!(ØDė]{õUœ? öō¬9syķĶ·°s}YrūŽ4kŚ”jÕŖe?·bÕ*ƌŪ[āćōfd\)b{²UĘŲ.ųõ7ī¼uķŪ¶„}Ū¶G_ł„¤šźo²š·ß*UlKüZč4*"R~†¶atė78s|ŪĪÓ ‘pī5 LÓÄ4YωČ)bć‰ŪĮ²ļ§šźO±X†‹Ę}zÓĢ Žś ‰qüĀ:o<Q=¹l@‚ ȈßO¼;Šźžnü‘]Ļ„{ÄR~Śō :¼Ō‰ž½jäūõG½bė–3{Įæ“éS 7^’“}gŽ$āĶFœwĆCœwåv>xd$S7nē÷å{¹ŗI}Ó$""""8N’}>l’½æā:·÷ŁŁēüųćQÉ_€÷¦½ĻÓO>Aļ³zŸĻŪ“§šļ¾}%k‰Ū6>Ÿ§ĖU)b›åüK.=ńW9•4¶™^»n=@—ĪØ Ąž½{łcÉRf|?‹Ć‡WŗŲ–ųµŠiTD¤œ3 ‚Ć"°- Ū¶ü••9'^2sĄ&†iŖ ƒČ)c±iś=üēCž _fČpR½ó Ü?øQęÅn­Ž\Ńū{ĘüøEoÜ͐©į„i$&pīčɌźäæąnĻĄ³k1ļė½dx ŖŻŸ3#ó’oیīĶąžßńōüƒü2įVžx;œ +…“V·ņĮ£EļĻ»åsī}p©Q5©œĮžĮŌŖ©z""""’såiøœN<éé{½Ļ¾śš+† fŽĻó™ĢAÓ¶M:“kNJU«J|Œžōt\nw„‰ķÉTŁcĖŌé0uśŠķ1RXD¤4ĮÄ0 l€­üÆˆČ =ńfž“łĖPļ_‘“ĪM•QDģ9@BZ‡‹ąČšŌm܊®}Īē¢ž ĖśĻ҈ ėķOóTķųš§lž7‘D3„ź O£†+—?éź¦ÕłiõżdÖZ1 üO{ ,fDŅćΧy<ś}>ły5[$P•įRб?ŸJz”¬ß½“-ū„E5£wæ«ø„WøĄ""""’·å@jJJ‰iß~7“oæ›Yär›·laÜsćėųŅÓÓĖmŻc‰ķɤŲ*¶ĒĖčwŁ„ DD*•~9‰ i%~å”|ĘŪž’ŪžašWɜ¶lĖ_ÄƲ|Ų–…eYžß>ć2Öē Øˆˆˆˆ³³ &,²zę8¦‰išžß ĆČ|l˜Ł_˜5 3³p’ad~•Ö’„Ś“q]µnń,źÅĔėx'ÄÅ\ęz,fx<¤¦¦”Ų*¶Šķ ¶sūvZvP¢uŌXD¤Q2BDDDDDDDD¤ā &5%„Ģ%ŅŅRS Rl[ŶŒ2uś){\n7¦i’žšZfŽ)-55s¬×r>ŽŖb«ŲVdJ‹ˆˆˆˆˆˆˆˆˆˆˆ”QĮ!!¤„„įózOł±ų|>ŅÓŅ Ul[Ŷ SXDDDDDDDDDDD¤Œ2‚BBHJJ¶¬Sv–m“œ˜HPHÓTl[Ŷ,Ē_§N‘²Ėķvćv»IJLĶ퓾Ū¶INHĄå?ÅV±UlĖ6%€EDDDDDDDDDDDŹø ą`N'I X'1™fŪ6I 8œN‚‚ƒ[ÅV±-”)‚CBp÷$Œ­źózIŒĒét¢Ų*¶Šm9įŌéRDDDDDDDDDDD¤| Äaš$%&H@PŠ ŁOZZé©©ćPl[ŶQXDDDDDDDDDDD¤q¹Ż„9¤¦¤ą‰'08—ĖU*ŪĪšxHMIĮ4MĀ""0MS±UlŪrF `‘rĘįp–ųJÖ¬uˆ IDATq»Ż†Q¢mٶ'=ōōt sÜV—Ū­Ų*¶Šm9„°ˆˆˆˆˆˆˆˆˆˆˆH9år»q¹ŻdxėR@¾×VXDDDä”0Œ£hFžv[®)#wR8'!œµ\Q‰`%EDDDDDDDDJ_‘ ą“æ&~LśęY"Ÿä®]Ą~õ∈ˆˆœL†‘#ĢĪ“øµż fęu³“†);g:’D°’Ą"""""""""'R” `» d­}D^¢7ߤoö6üĻuO±ĄAƒEDDDä$ŹÓN+$'kd·õŒģeJēIē* Oo`%EDDDDDDDDJO ąĒüµs„wóIü™ōµķ£VĢZ*ūéœ‡Ź‹ˆˆˆ”†mäžČyˆ‘پĖ=;ŸdpīD0žŅŠF~€³ŪžJ‹ˆˆˆˆˆˆˆˆæ|Ą…•}.(łkcg'~ķܽ~ķ\‰Ż<ķ#·žßč9ŽH¾Ś¹ ?Ū9Ėd>Ÿ3°‘ŻŲ湫@Ų†æĒ°Ó8§'°ŹA‹ˆˆˆˆˆˆˆˆ”6gń+ ł{TÆß\Ļ™ ¶ó+ •8Ö !"""rJå;ö/™‰Ü¬D­5žÆæż–'!œ™¼Ķ³H®ŽĄ†Q¼$°ˆˆˆˆˆˆˆˆˆŸ£ĄłŽū[Xņ7æÄo®Į¹¾öQ„”ķ£v•wR™a‘“Į82käi f.a¶įĻż9 įģßäIźf§‡ °ķā%Õ XDDDDDDDDäųŃøąäoNÉg2×¶±³Ą9‰ß¬¾Y‰āœMåō Ī&X _‘SįØV˜‘•Äåč.½d%t³ >ēŹ“³^VĻßģ1ƒmõ9Ńņ$€ķ°GōüĶüµm+oÆ_ŪĘēóāIKśįĮņy±³’Į™;ā襳”TBXDDDä¤Čæ×­‘ēaN28³Ē°a˜'N—w`‡3g ąģöéOśfn#oOąšź,""""""""rĢ é|DļßģōmŽ²ĻŁÉ_ŪŹ\ʲHKI¢I£śŒ}ģ~źD×Äįpč&žˆˆˆH“ł„?»÷īć‘1ϱeŪNƒC1L“Ģžæ&6YIąœJŃFvĖR½€EDDDDDDDDJ“³čEģÜ՟ Nžś{ž¦$ĘńōćpĪYŻńł|ų|>222i‘ Č4Mj×ŖĮ“7'0÷ē…<>ö‚Ć"ż=…­ü“Ą9Õ¤•ų‘DÓD¤¼;ÖJyĪÜȵ¹\½sOgłę’ēˆäojr"O?ž}zu#==]ƊˆˆˆHgY–eįółč{vF{‰ °¬%0Čģœ9&°’QN˜œoFž¶bÖ£Ümʬ 3"""""""'BŗM‰%=5UĮ‘r'--ŲŲX¢ź6)ńŗ–€Ī=öoīŽĄ¶æ “łĄ_ŚĘ“žŹčļU¹g‘J,##ƒ'ÅÕ7ßMÓ‰]:KV/ąÜ½~³Ę)=aUj§s`÷fŅ÷ļ?¢ŖˆHŁeA”D7:°Č%^ß ł—Īz˜óœ=ž/žŠvvho†‡Z5Ŗė©ä¢kVĒ›įÉ,ćŒį/ž Yö9s`ržÉŖü¬2Š"""""rŖX{˜7q"Ė›ÜÅØµŠQ:SŹ‹°*5ü‰`‘ŹĆYš,;׿¹žµó–~Īź lł|Ž ³m‹ķŪ·±oßæ'½<“Ūķ¦fĶZÄÄ4Ä0ō±-"""r¢™¦‰åóå|y0;Ū›łåĮÜmĘ\éŽģG""§JūöķYµjU‰Öi×®+W®TšDD¤lÉųG:^ė}?cÅ gSaG?µŁöū¶FōäœVįĒwEįŪĘw/Mą·į×1r@-½‡DD¤\Ė7lē~dQ²ź@g’Ų¶eYīdūöm$&&ҹsW‚Nņ`ė©©)üõ×¶oßNƆōŠ‹ˆˆˆœ–eł{g5&żķHĆ §uiä*m(,"§\I“æ¹×iŚ“)›6m:ę}Ūū¦3ØÅuüŲa"kēŽJƒ“żże{oõįžųū™5ēIŗGä:#§ĻąŚšCŲõō~¼½n™éåŪ2ž-ļē£F¢ äŅńłåĮzS‹HÅcāÓ”§sĆ'{Iõ8ƉjŠ’3Īčwą‚¦!™Ė™įŌiڌfu#qTäxd,åł!—²jä_ō9ްˆˆHrtøĄņĻd—{Ī|ÖĪī œ=³ūöżK»vHKK%%%ł¤ž¦iŅ“isV­Z”°ˆˆˆČIćoCvvig#WYhŹ7ß2Њžˆœā3X1džĖ]Ń`óęĶDZGėŽ}™9īš„-|™×ć¹n§äܝ°dCnhÄĀOƧ‘³læNŽŗWóĪÆ=HĢž>ŗ—æß敛>«KæNz#‹Hå%īĄ!¼Żcöø¾¦Ē³wó¾ļ9uxaÓēšŚÅup8ZqŪēær›&""R)™G^ģ<…æš3Łc’f=¶³ĒΟĒćĮēóŚKųD±, ŸĻwŅKO‹HŁf%ģ`Ł‚Ł|öżŸÄŚeą€ģƬūyÓߝĪü=–^ )÷Žj#ńEĀĀŚœ…}±PD¤BJžĒÄ7·pö˜/Żkļ½2#oÕNdÕŪ7Ó³qU]„×nĶÕS·cxęq[@ŗæ°•¬V¤µķ%zÕåöŸ=…Æ›€ö½iņė]\1v1…~};c3ŸøŒnM¢ ®J£žĆxcY<66ūæŗ–śA­¹’×$’²ėyń¬Hź\ń {­R<ęĄhZŃ•®]3ĪØ¹–©żĖ9ϾÉMz_‰H…fVkF÷=čuĪł\>āI¦žśļō?Ģ»7ßÉG{mš­aLū š?“oQŸ%¾łł¹kčÖ° A”Dv6O-J÷ĻŪĆć®¤Kƒ#‰érĻĪŪ‹/ū&Ė.¾ø»/šDäĀT…˜nCyłøĢ–}Qó ż\)Ęń‘Įļ÷†Ć00Œ ’R¼m¦māÓūŅŖfĮQ4ļ7šy‡u-"""CßēµxtDłgrnźå)]ŸĻwŹžČS¹o)›<Ėßć¾'`v=ś·„ź©īmꯥēć_bvj4Wv¹‚³kkĢr)ēŽ.ģĢžr¶}tč<-Ou’‘JwĀäĄ7“ųŒĖųšŖ®t©5ˆ±WæĮĒ’ ā6hߌ ÜpēLźŒžĘüžµ±öm$¾^t±J1—t]gÓį¼’T#Ī|wvXÄ»ÕĢē̜̯䲏šĄ„Æ˜X7–ĘŻĮ=—ÜGĢ_o3ąāń¼4ø#×Ü:–A‚³Ś~ĪuµŽl£Ķ‹oķ¦ßkæšųąź@ū×v0»É>ūu"Öą’^bPĒk>ü U¾ßČyo|Ąhó3XOåłO3øxŹ“S\D*)ĒihécΆ-ųØ^üskÜ Ęæś­YÅäQĶóÜ0¶Ėų×7pś#«xėŽę8€Ž=›ø¶ć_ųŽ‘&Āßøk~żĻéŒ“Žœ]g;?tŸĪ÷+2čߣšłēµ)ās„{ĮLJæčcP­¦“nŻ8ū @E}Võ?ć&¾æ›3ĘĢåĶ;e®×« NłžßōV‘ Ą<²tóQ‰Ü¬ń!'Ńk±¬M‘=€s—<?"RłŲśķgüždūüŒ9ūõß³ˆH¹{Ȑ\mĘģÄpVó±ˆ¶ØŚq"RŃy׾ϔå-¹īŚ™_ čĀ ×µbćō©üįÆléź>œΉćå~mčsó³|¶ņ@f9Ļb8¶uƒéōŠTžnµ€‘7¾Įƌ#ŽyÓ*Ö$'ņŻu $00ą&£ųÕ“ČŽ½ ™ßė©1ˆń’ĄŽ&³¢Ė“¼0$ŗŲ½rK~Ģ^VMy‡%µ®ā¶‹ŖėėD"RÉŪį%?·flXĘŹäzō<«ńQ½…¼—ógJę¼ģāśĪ&œÕ³É«–±©€“#ę4bĢCŒµŠœ_Ō犧ć+š“”ˆmfl^ĶzO=ŗuWÕ©˜œÅm1ŲyJ@Ū9I_õ‘%u9Ÿ}·Ÿ³ ]Śd銕|>c ÜŲ$ēäåū›Æž÷:3Öļbßįx’Ņ Bkµ ×å#øćü&„ųļžŲIłź7ųhįö{ĀØeįÅ?ŗĒĢÆbÜéT»čy¾Ł>¶LĪ S¶įģ<’ĻŸ;ŸjĒs'É:ÄŠĻŽåo±~æ‡ąčVœ=xĆ/lAxAŪõżĶ7ĻæĮ7mgρx’}Ōlv&ē÷¬ŹęŸbéÖĆCē‹Fp’5ķ©’µß!–}ņoĻų}„ÕiEļ+†3ā¼Ę>öż1• ļĪfłö8¼Ōlp6wŒ½‘ŗ]&"„#+Ńkd—‚&³—Ææ“§ų³z’ŠHe•ĪSŽgmŹVlįäĮܳĢ=¼÷Óz …€6Ü9s#~œĘk/N`Xēń¼ņŌlę>Ō‰ ĆÄįoFFž×六[Ų”¹šsŪ;Ļ3÷Œ‘ÜņźCŌĪs’·ĮŒęŖÉ³yØcīŪ &”ŃU3ĻčöaV-XEJX8,łœŪn`DÖø¼„}ĢŽ•|śŁ\ś.]U·SD*1ߦ„¬ˆwpZóÓpWģs«Ó¶ ¹·{l÷} § '>«ó‹ś\Yg~ ł]JµĶõ™ĆŅŲ–īj‹ˆHÅdš¹^仝ktą<Ż‚ Z^=€E¤Dlb|Ėχ °żEÜ]j™>¶ĻžĮņ“\‹YūXóŪJ6ģ:@2!„ŁÄļZÉ7/>ĘėĖSż›:Ą¬gäř«Ł“ä 4ŌĖŽŻqd_ƒœŃ­%.Ćęšź•lõö!V­Ś‰e8iѵSNrõ˜ž”V¼q÷NšĶŖ=)˜&‰;—óՄū5}3­gķćĻ…+Ų°+–4W8įī4ö®ĆŪo|Ä/›q9H=ø™łļ=ÅK żgįV¾u?£Žžµ‡T &åŸå|žÜyęēŲ̄̔¹_/Ę}#ó~ĮӈęŅg§pg‹ƒ|q’0^\q€_ŻĄ=ŚćŲ5Æ–$`9ź2čÆ1ŖSÉsį‚g~÷_TėŅ–Ī•¬Śµ”Å»n YÕÕ,ŪčgsĪ<#Ŗr@^Ö½u=Ž:ź $ēO9šS¾ż‡ £ē>ņ ŸÉŽÆēęW—°ž³OųmŠ£œķ.$F4—>3…;ź’Į˜ėF3'ĪE—‘šĀßü÷zž_Ļņ%ńöź„ćĄOLłf^WKF¼õ"Ccœģūīa®ya) f,ą@ļ‹©vp7{ŅmŒ€öÜ2a—Ōp`y½ąŌŪNDJ‘ķļLę—ń ü…dŒ¬1B×U‡`)o¢¢¢J¼NüÜé|}ø ßŲ›ö r·8-Ā/ėÄų'¦ńĶž!\›4“7ē[“>½!žĢ_wŖV§Ŗ 8šqĮZńŌ’dD»t†¶­ ŪÖpŠÜ·õ»‚×-’IŻ«_ęŁĻ;pć·9©Z£śÅÜsć3 xžr®v>Ā gÖ' y'Å6ęś”ŻKų‘Ēīž˜Ŗ£ro‡Óq¼ń_žńwMČw7Ē`–ź1ŪÄ.ł ĪNÜ׎­7¢ˆTžŪ&Ö±`ž|=‰ģŪ¼˜™SŽā“õ5øqś«\m’“­-śÜjTæ˜{oz†óžŗ”kķGڵ6®øķ$7ĀŚ^Ä}·7§Ļø+:†kZ٬™öĻüՒ{&^ą’÷ųõ¹^ŲńŽ”§ņŹGĻ2±Ė­“±wp ź".ļVÄ6«ü‡ūļhIŸg/å FsKÆś$-bS’nr‹ˆHÅPč%_īŠy¼6¹>jé|·„Ą"R¾æąūõŒˆ®œ×%ĆqēöiˆÓNfń¬_(p(`G­ZŌĄÄ&>6ąūēo¶{mĢ*ķ9»mątēķ`ÖģĮ9­Ż¾æłõ=¤¬^ŹźtĒi=čY§°S„aš˜¹ŽHjų¶¬gƒĒƌč̽jąÄM½~ēŠÖi`%māÆ|Å» kJ«°-RSÓ°Ķj4oZ›¤„„ĢæuĖ:6¤ŪŲžu¼q}ŗŸ}.—Œ_B²mćŪ·—}>p4čB÷ŗ.H[Ę ×^͈qӘæ3]¹)eG“³†É3Ū.F+RDää3 £X?¹½öŚk%“Æé £Šu‹s× C_zŠsó NļēēņõCŲõŽ= īw.^’8.ŚI‚Įź—žĖnął‘ķ œĶogüµ™÷ä|wŲ.åcö²użf|µO£qˆŽ»"R8‰Ø^ Ēćø OĪūĻPF½:¤.’å«‹™tqœ±zs)üÜĘY’›Ć7ĪßoŽĘ ¾żtŪ‹|żW,Įt3“ohĘź’]C’žC’WKśv&£Ļ*„æ©°Ļ•"ŽĻØĘąg_cX­ÅŒ܏ oüŸ®ś«ČmŃåÉŁĢzŗ'±ÓļePß>ōæņEÖ×ķEĻXDD*@‹!ēź³ «Ņ|'ó”óS‚UDJUkg’Ą6Xq?šął?ä™ė[3›9’\ȵ ņoŽ;]žä®•3FŒąõ\nوā¬sŪņśŖ„lZ0Ÿ91+I°4ļŁƒŗ…¶ś“øé]ŽŗŖž’ā •¹^ĀæęŚS©eV]ø\™cŌųüƒč8]žÓøå/®jYX€Иs.ķFŻ\W}Fxkj8gn{ł%~ņŸĢśƒ5s§°fĮ"†½ü*76SŃ<)E¶mų{’fźŻ[Pw_u‘“Æ]»v¬ZµŖÄė 2¤d;3Ŗsõ—q\]Ąl³Ń=,L»Ē?õ0?nxøąm6欗ąŹ—ó›YÄŗŁĒS“[ę¤qK~-ކ#˜sxÄMÓzōōCś?zōņuGÆ etīg8cÜŅʕņ1ūŪČ]ŸßLśóz’ŠH%aTćņvsłE,ēhĆć+Sy<ė^I÷"Ī­®zœ÷ČGœ÷H~ŪŖCßG>¦ļ#ÅŪךMź5ž‰¢ęž¹RŌń¹›\ͤ߮fR>ėŗMGMzŻ;™ł÷źm%""Ļq~™ÉĪēQKGļŻó/Ģł— V`‘Ź"m³ę’‹e8 «Yśõ²~źP-ŠĄĪŲœ·ą+ęę ›ŠŲi`%üĮ7?ģ) lP­×Ī ļś™ōó!lWKśõ®{Üßśt4nIs·æ”ļģĒK;ēžÄ*ƍrĶė9‡ ‘}R¹7œ#¦1œvFé5ĻęźaĆøåĘaÜpé@.>æ35 Ą›Ča³ ēĆ“'qS 'vŚß,\²;Ļx9""„ÓJ,NKQD¤lX¹re‰Æ5W®\©Ą‰ˆˆˆˆˆH™RpŃ'ūčĒy«@—ģF^i$a•Č©’—żČ‚X#č īyó)d—›ó²īĶžŃNvĢū‰u×6„M1¶gF÷ćŚó¾āæ3÷²ąłė9’Ŗxbɀ!‡ĶŹU²hž¼·s;ļź ¬æ"""""""""„"ŸŽmł'Yķ£Ęl#ß$±ˆČq±łż‡?ˆ· ‚ŚŸE·ˆÜŁ'Ķz÷¢¾¬½æ0gMzń¶i„Óķž <7ģ\ŚÖĮ›pxoÕė5£SĒFäģ"€¶_H —f½.8‹j„‘Œ0BčtŪxžæ±­j’‘ź#øöéœūs¼x}3Ģŗ už^›0G0‘Uޱ³I‘s?ŚÕĄ™ž@‚ĒMT£fŌry°ŸN½śįxngÓÖŲQ-č{ćhFö‰PīE¤‚kŌ°!«×¬åĪ‘£ˆ‹‹#>>ž»FŽĒŹUŅ0&¦”ĻēŚv“‹;üˆˆˆˆˆˆˆˆˆˆ”ˆŃwČšœž½v֘¾ž’ĶŲ`[Ų–e[™ĻY¶eaY>’o Ū¶H8|e g结 ~¦eĖÖĒ| ¾€o?’䘷±nŻZzõź­W\DņeŁ6¦q‹žēĘGē°æĪޘ<‚ÖWD*øųxFÜq'’ģÜEćF 1 ƒ-[’¦vt4o½>‘ŖUŖ”ھ:õģOx•ꆉiš¦‰i:0ü ĆĄ4L ÓĆÄĄĄ0 0ĄČü‡¬.Į†”ƧˆäTCŹ}ŻfcŪ`ūÆÕ°ż×mVętžk6’5\bÜAęĻś¼\ĒbŊe pŽ"""R.͚õ:t*×ĆŁY=ū/Ļ5Ÿad>6L02Æó ĆÄ0ȜÖõžˆČIQ@ č\%śŠŃ £“K3g%|‹zžxĀ""9R˜’Ōu¼øĘ"-6Ž"ésżZ*ł+"L•ČH&M|•wÜÉÖæ·]«“&¾BµŖUK½MgŪ6E]ĖŪ6¹–Qh‘ćå,tn‰nŚElŖōĖłiL`)V,q©ŸO9EJ5œššŖˆŠˆˆˆˆˆˆˆˆˆˆˆˆˆœ"„šNLŒWDEDDDDDDDDDDDDDN‘RM’ż ETDDDDDDDDDDDDDä)ÕšūBET¤w4ŻÄ€(""R)¼1å+ADDDDDDDDä0‘ŠA `‘ B `‘ B `‘ B `‘ B `‘ B `‘ ĀY*Ųmҹa0mj» õdß¶ÕŲé‡iŪŗ9”Õ’dFń÷Į4žÜ™ŹŅm)¤fXz%EŹ(ˆät‹tƍ×gcٶ#"RdŽÓ}6‡½øķźģÖwEDDDDDDDDŹ«2•®SÅÅąŽUéÕŲĮęõkxį™˜5g.‰I)ąpPæFU:wkĮÅēę‚ACč×ŗ^ƃłųlY,{ā2ōŠŠ”^Ė&6ŁKbŖ…¾""e‘adžtŌ©ā"-Ćāõyū1MŚ­Qa.IDDDDDDDD¤œ)Ż;œĆzFńĘ5õéŻÄÉÄ /0š¼ó™?ū;Ŗš“ØFŻ ŃN6®ŁŹ˜±cūß»Iüw'.'œŪ*‚ICcÖ£:n‡Q&mŗiwZ8¶ "²,¢į i“Hw«Ź™śAJWl²—yHHõ)ł+"RŽŗL.īIӚÜóń?L[tPgq©“Ī<óL Ć(ō§G ”ˆˆˆˆˆˆ”9§¼pķH7_MĆjų<©¼óŹ ü8}2£zĘŠ Z0ՂøL £j•ŖŲuŲüÅėxzĢ#Œ}ńU܁U±MƒK;V„U žžn/±ÉŽ2hwżźŒŠyąkÖ„wŖČ äĀsjŠĒĮWŪó[¼nļŹń³lŲ—ArŗOĮ)ǚGrm÷źLśy?›÷§óŠĄh•…‘JgŃ¢EE.óŪoæ)P"""…µ‡y'²¼É]ŒP ]‰ˆHyvJ?Ē×`üeõhX-°X’ēļL{÷=nīՐfaРЦ^ AĆ`7Ń›”Ć„īßĀéU ż_‚C£ų|śū`„e÷Ni^+ WŌ'¦zĄ1“Īc#š2óī¦L9'„ ²ņJ\yUfŽŻ„ Ü¹^8“³/hĀĢ»›ņ~Ÿą²9Øs™Hhķ& č\‡ė{Tćŗ3ø°Ī>ÅļxŚČ6ģ>ģQņWD¤‚hĄµŻ«³bG2÷}ŗ““ }YLD*'Ū¶óż‘²šAČ¶E3łńƄćÆ^äŪĘw/MąóÕńŖ„$""åŽ)K׎tńŌÅu‰r`ł2˜:y2m¢Ć³S‰ $,$ˆ@— §#—ķĘeø-/¾=Ūpā厑·ńĶ7ßsšĄ> ,’– Ŗ‡:yņ?uØ\ŅtžAL›*tņēŽ«7«ĀŁaz“TDžšśœ³™Œ›ųfY<_o aSR J]»} ¤{-BD¤iS7ˆN1!ģ8”ĪÓ3÷č&ˆˆHi³÷ńÖyADt}œEGVeJŸĮµ‘ōyme·•ķåŸOÆå“€ŗæ°] ˆˆ”ӏ£¤õ|ńÄ5œÕ"ššuis匝½»ģŸŪ3–ņüKy|ö]ƈˆˆärJ:<8 ½ ¶?łkcaĖŖÕibärąĘ‰Ó¶qįÅa6ų¬ šxƒŠˆ`zŸÓ›ōt ƒ¼ėVuņŲ…µyąóx}ÅüųwsQ›¾tVģqҾ^¶ą‡ßÓÉ.(m0°oēÕtS=ŲAØÓ&)!?VąŻµé¤ų3Ų3ŠKåō±;ŃĄ žF“ƒs/jČ= MÆŁÅ°y)d`аk}^ī€oĒ>†}Ļįć ²į¤Mūź\Ó&„¦a) i,ZyikŅH,h3€žēFŃæ–›Z”‚M›ƒū’ųa«—†MĆi_Ż©V®9ĄėKSˆĖ «é¤]‡ź\Ó&”Ę!ŸĘÆĖ0u]:©™CTĆj ļNŪŖœ^bywʧžš7¾U<{Xz ‡m/¤ÅOę`ČŽ€¦ō브ø%nVxżÓKˆ]ÄJ«)-Z{iā#ŲįĮ—rM[³8”Z„ndNöŖēƈHuiĒ*üµ'•;’™¶č ×uÆ® ˆˆ”*›„%ćrC#~z=œåēø̹—óļś™ō C/£ˆHyżŠū…Gś^Äs›ėńŸ[āõn1„¦żĖŗßē›ģBgx‘ņé”ō¾¦[uTĖ*ќٌHIŒcĖö„¹q6N8 .L‡ Óa`˜f`N‡ OR<¦mŠ«gw~[øŪ²³/B³4«Č«ūø"O‹ąĢHŪĒÄÅ ģ³ źµŒätW®… '-sZ'ĮųHČ0ˆØL’>uø”¾™½LŸ~uøµUµl’Ņ jE:rŪĒŹķidu‚i`†ƒÖuܘŲlŚžrœc›“éY—§z†Ó:ŅÄē…Š*Į ģS—'ĻĄUŠj†“V‚iRÅI€å#ŃgP£v8×ō¬J÷(o†M`hgvę–Ę’+gŅęĢŗk× o²ā#eB°Ū¤k£¾Yy˜CI^ED¤”“ļM“_ļ⊱‹)“±ƒ™O\F·&Q„W„QĻa¼±,›ż_]Kż ÖÜ’k’Łõ¼xV$u®ų„½ą™ĒmuóōŌµ¶½DĻ ŗÜž³ģDV½}3=W%Š@xķÖ\=u{=漛ßāŗēÓ{ņŪ\S[£$Šˆ”O)üśäĶ<æ±5Oüō_’Fk'sĒå±7Ū{ĄŚĶÜї2rņ:•ē/zœ†a@ŗ×fŹo‘Ҿ>i:œ÷§\IÜsWqē·ū øLęׇrŁ»Iōę+~™7…[ŖüČ=—ÜĒģƒē„Įń¼vėX~OĪ`ćė·óŌ–ž¼8aŃÅøņ÷­Ą wĪ$ā¦iĢ_ņ;³'?Ā5¢óæiąYĆ 7l+_6zšbұeÕ ŗ¢µ¼lü׋„;pŽ*Ōs€šŹ¢Ż>l8Ŗ µ˜Ä½6¶Ą1n‚źÓŹ ¾I,.¢ūÆmƒ•ūēțQ4q‚šĢ[¼x±Ł½!‘u4«R¼Ės;=’gļ¾ć£(ŽŽöjz'!…JH½7„(]ł Š ""6° śU@Q¤ˆQ¤ƒ…ŽéHļ B萐Žļn÷÷G"F/>ļy‘½ŻĢĪ<3»{w³3›”Ÿo;“4±ēņĖź`—_VC;Ŗ@ŃŪŃÆo5V åŪvŽ8zg#°]ČdkŠFGž{"„ńܜz»ņśŌa4Ių™EėsAń¦ĒǟŅ3e2ƒ ę™Ń1t˜ų ½|Ė÷±_M*S” !ÄMųÄLż7æćżˆµ¼2ąsbм’±ŚÅŽĢt–ĄŽĪ;;;Ŗ½Źś¼tΜɿOńīɄ;qfŽ7ģh4š{ł–ūCæ±é ^o—¤öµh;p‹v&PÜm®ź©9¼9!…ž^¤¦IjM!ījZž·:ŠRņÕ³ŻY•hŃŖŹ„Ļr†j“jįOę®m*aL„>ø*Įŗ $&©e®/ė—½™ły0”³he„i9¼‡ƒy•hŅ4éņBq/2ÜŹ5qÄl(ęŪn5‡c[~”A nؘÜUžöóĘØųˆ«½Éļܛ–ŅéœvŒ\ĻŖ„…×@gŠČłsgš®čWģ~ķŒ:;²öP #Ø “ 5¢C##ŻBŹÅ7. ŽNF܍fŚT·ć‡M9嚆Ņz!—c6ˆ°s¢cX»÷ßs‘|8­MiQу'=u`ĖfĶ”¼ėžVԚ˜Ća« u칿Ŗ=16|œ‰Š–—Ėį‚gYŲ z©+8ėPĪŲ®©óƚ”K¼ ĀõzLéé,Ž›K–F{#.6 4@§ĒUĖå·õ§łm‹™>=é[ŃLć #óĻ]yÆ…ĶŁ(»cœŹ0‘¾ĮۚĘa«;:5•3Ł5h䷎˜i&ļ5Š—™{5³Śˆ;…uFo7OeöŽ'^ß,A¼ŪčkšŲŒ?yLņvÕTUeųK9`9ŖŁœĄŃ^‡—‡wW=z=¤„«$\°–”¢tś*Ł1ŲūweŲØØ.mGÜ•+˜łć`žļ[ŽfŠ6ĢY‚"„7š1Œē¾ĻÆ _į™ÉoR讦Ī—GæYĶ›õ.’(ÆĆÉ×#’¾2-™]kw‘åģ’,fY\ž­Ršu½¢CÆ«„„G™kńāŠ:ż>‹©?į©ųģ½ÕüśfżĖF…i$®šĻ/ ѬnķΧÆŁ,Ō7#ذ{§guCŽå !Ä]ņ1Ķæ•mlŲµ\Ś`WģVŚ5}gØŒP±©åX_Ö5ī€ZzŠ»¹ŗ¬4*łeSe˜€Bˆ{Ó-½Į©N%‡āÆŃš“¢£mŽäRÕŁ‘ųėm„łøąbÖ°·s@oŠÓī‘G sVØlIĄ»¢'ŠNįųńćæ$/éŖY¾‚œiāZ^&_Ī;Ę Y—~ŽŻ“‡ …€PgBĖ9dIKMcŃA VEO“ū‚™3°2_·uÄXt»Ü VĘXŠŽ&Č9‘Ęŗ“ė³–žĘ¢}yX-;†0’Ł*Lkķˆ“¢qtW2[rÕ‰ MŃÓ¬}/„\[SŠRŅXmĮŖhÜ&ˆ¹ƒŖ0ēŁjü00ĒżņėĮąķĮGOWaNæ &=ģK× ło°Ī„ŚnŪHL£Į w’Ś×õį‘(=mœ²ļ ‘Ć6#hē9«ļڐΠ½y¼ÖYÜ2m¤©r?`Q¹Ö«ØĮ¬5|’ŻQ ŸĻ‹MĪšĆW+I-õĻ-ģ|'‚ź>öTóqē…%Ó¼[޳fücōj@ķ _ŚtÄÜ]SÕjéœż}śįļJŚõxeAüe7”ęæŌSüņöƒtkDŻ@gĀżhßūMęĢz‡”Ö¤a°;‘µńā”…ó~­y*5æ6āfõ¦CMo"ü]ˆ¬Õ€Ać~įÜå}īźy¶NHŸF~Ō p”võP:w{ŒY{¬eēė¢ÖæBÖļršß›_ņ~㭚ī<>ótA^4’ēõ¤vÕGY‘¼)ķÜi’žöĖnˆ))®*gxŠöįžŌō„õOńŻöė˜VųßśiLT 3aEÓ““7Ū96OéOÆś¾Ō¬äEĆF홺5·`Ż6Nz‚‡¢|ˆØT‘ÖŸä‹õg ßč“{˜•ļt§S„'5‚h’ﱩhĆ-Wœó%œ‰ēč4{н1›wdņdžt,KāŪEXø<™ļ¼€f×gƦlŽ‘Éę™l:ha’īEoG)£”³ņ–±¼ū+MŃś)OŽ.掤mäćöŽ4ķū5q9’ŽŽ—¾4‰KĢ•€!ÄM¢īĒē·%fģXV\6į‚”Zmj˜Ļ³ū°Źaa„]ü %ĄUh\Xł:ĻĶófğkyæöŽü±’^§uØXA#>:¶äĒ1(NT½’9>Yµ“ÕC½łgŹ 6ęŚĻ^_±ūĄ>vļŚÅ®]»Ųµ}O…˜‰|yʵ“›g…ānāŠ–>ݽ9=w_Ē’ßP½‘ö'X÷÷ŃKŸ—­‡Y»ž‘õ½ËʺĘŖEa>Įŗæ\9;…bƒ=¤&§dRfšÕėiœ?;Hž“!„÷ [:8Ų³„ū€u&*7}€voĀĶjÅŽdĄ ę–GŽfĄ 0b%'+ ‹ÅŠĪjĮh4€¢š–Ā©S§‰ØÓøō}{•t²žśÕqQ ēd[ =jPćČ” NÕõ ŠÕ™6~‰<]ž’ŚŲś× ŽM÷ā‘pGŖŗ0«6’’-;‘KŚeéļߓŹįZ^T×ŁŲ¼/äi•]ėN2:«Õt¤Š“BvJ6›÷$ņķ®œü75Z+’8kšzk¤e]k—Œ-ž`tŠŌp¤Ŗ«gÕʅÄl :ÓÉ*įīfŖ8kd¤ēš÷žD¾>tū:€ÕäC¬I.ÜĄżb½(鱬ßė/®÷ĢoŖbóF’Ž ÖY±į²å’š¢Ļ¶.™FŅźÆXÅC|ҳ!‘ŽŻ˜:ųK–ŸźĘc%u¬ n=€Īžö@&ŪĘv焟ųīBFł%±įӗÓ’Mü×N£łÉÉ s>ÆĶ`N[?Ō„C¤ūW¼ģN—āҼ<›ÉŽ“Äˆ1Lś¤ʔmĢ9‚wĒŌį’†aā›Ž$ž6–÷>xš)õwńVcÓuåÉ]Z~ux5Ą°iCńqŃ8·žƌĄøšū™ŲŁ…löLčʀiY“yeŸ×÷F;±˜^]Ą®S6žØ[j¾Z]øgGķę1/ŚĘždš ¶Ć›ŁœĒŁķ;Čķļ‡=¹ģŪŗ­Ī[Ōw‚ørÕU>ūŖ=2°ŽŹ ÖLł<ż&!ė¦ŃŅéÜæõöÆ‹9ėNq)Ķ+X”Ķž»óĢV:¼6—#ŻÉ;ē #Ķī»3h†Bē’Ķ`X˜Ę”…ļ1±or~ž‹!uģ@KeżČī¼ö£ =†ĻdDug’™ÅōŚeG|f9ć :ŽŽmł"%•5k֐™Yų„÷^õ£s;WĀŪķĒĶĶE‹ZߤŠFPpHŃ héķ¤¬˜9–§ŒW±æ›TŸZ^ ž{Œ9ŗēłjŚBŒņīvp²»t6MĪ“J@„ā¦ŃšŲ$Ę-ŽbĄŅK]µŠWw†ų€Nćį1Ć[ōoˆ9óū“ŖŠļń&8§żĪˆ!óńxu/GÕF’ł~l8‚—¾y€åƒŃé«Ó¹[ļ}ųĻÖÉåńHˆŪKbĮ·å¶#ĖłbJĶŚ•pĢ;ĮšÉąį…G‘·ėz×JTw½üCAž&;ļŹ„ś9!Bˆ»ˆāʃc>„÷ß}y¹ekv¾4źądK"n׎V~™quåµēĆh;¶7ƒœŽ„o„ĘŽY#ų` †NéŒėČF×8Æī¼üōtxļ!žŠŽęńĘ~SޑҋnµC‰ŖmĒgóĘ1„Ń`jiń$TčŹ#MŹHÓ½Ć^ØAŪqћQ<Ó2sĘFeȈ`!„÷†[ŚģėVņ7¶ž”‘8;»’›—EŃ”čŒŖT§²õ4#ē-ħBjēZIs²Ērä4¾A•š®č‡Ļæ“ci7Ųϵ¤}ŪX»ņkKZ{>‘Į“/{%“§āćĖ^‰ŪO×-EžPµ²żŸ³l’§„76JžL$Īī&Üt`KIcł±R¦3Ńr™7÷0ó®X”²fłaÖpåžwl=Ǝ­%×Gvb*Ÿ-Nå³ĖfcõO‡Y]޲īŲv–ۊßOŽé$FĶI’£ķ¤–÷™¤ź1–~÷žƦś\čā IDAT±£‚”Ķŗśtbž¼}ōV»ÄēŚyW#4¬ņ„gk§,cę÷§i>ī7^čā‰Ōųš8keՖOhfŸ@²ęN³¦­ØSĖØSfšÅ8ViB³¦õŠÓßøŸX3%œūŸčNs#жžŠ‹ķ[ā°5®Žī:ņ¤%•–_ēšū¹Æ`¶ßZµÜ8““)?nĮÖ¹!†“Õ|3ćĮĻÆcĀKµ1ZāQ¾U”+V­ī»tSŒsć¶Ōf’lĖä‘Nœß¼“N®([×qĄŅ™zŹ~¶ü“Fµ^­šŅYŹUWł·ėpģƒ÷×C4š>ŹŚęņ÷ -›^kž‚cµ“lQ=­hT1ž ĪĖO³Q‘MSW2cĘAB‡naÜąź…Ś™–²‚Ææ”śŠ-Œæ®q£ŖdF7aĘē«xź‹8%/ēūE§‰¾œ÷žÉ/[cw¢ē’Āör¶ÉĖć PÉī,ūöķ+öšŠ®¹3U+› ņ7±`Į‚+¶ pÉK:˜\ĖßNʈY‹°²ĖX4žeļļ×§õ«_}›qĒdā#Øē"_+ß.ަKgĪl‹*Bˆ›IW‰Ē?}yk‡r鯗 mĘ’ŹĻ^Ć=s(N×JŌ~x=ūÖ'īÓį|KV¼R7 ę°ē™šĀLZŽ~‡åϤ«»Č×ē1óĀ ¼óžćĢO¶aēīKp£¶ŌõŃc9·‡Ēʰ#Č5ŗRÆ æy:©!„ø§/9ž½ųv“MĒ~ČŒÆ‡1’T6³;žįxąÅl8ŠųŻ,qx•·>ģKĒóąło.Čė ķoP.J»Ę5ĮEq¦Õ‡æ°ÄóuF}ń=Gda¬ĘCŚŃ%2€‡ĒMemæ’1źįŸ°øT„ć» éÕ¤FiŚÓhōjVy¾ÉČĻ_¦ē{ÉŲģ<ØÖ’į®ņ\`!„w½[śQĪŽXÜ„SCS½ŌĄ˜~Kz"ŖMG†ÅB¶¢”ä¦įźäAˆ>ƒJuŚauRILŹ%żP4F'{<½|Šé’-Jń_ Ū›ī¤Ė¶Žfƒģ§`vŠcõ[’‰±Iƒ÷&[ō\~ŲFĻ uņO:¦<ōH8³ēĶfēØ_ĪŌ¶#»‰ÉJēäŠ*Ō|łßWUlvēÓŃ?<€-—0ī’ź³ÆĒķߏöµ¼Š_sĪõxūUDÉL$) pō>ųT€­iiם'CƒŅņ›Ė‰Õć?å'vʞ%ŪģŽ)Ê”~ž([ģ6dśÓ¦U8Ękˆ•†łāŁRń¾Öµ_gęŚķäu¬ĶÖõū‰üöŸ/bĖuLkŁr¢ -ŪUAOōµŸł«ąÆK")åĘué«PBšÖŲķČ ą¾¦•Æh¶#;ˆĪ*²ĪP•†ü˜üĒvā¬=Øqt/GņøæA„?ų]MœÖ0Ņ Ņ‘­»/ĶēØ(šĀ“ŽŌ©‘’؂I£+Ńē…8²s.•ÉŁQOæ®.äO ¹ŅŪIY1³•£Œ\ēž®Æ>UĪĪy–įY•øb"m½å#ųķ¤×]j͚Ü/„ųQ”›xó‘āĆ3æäšLqēŻgł%łŁĀ/+Ńńķ¹t|ūŹķFķ kŌ寘i8v/9c/{É®:}&żFŸIÅeęüżækųV”>cdICBˆ»ł½¾O3OZŹąI%mąĻżoĶēž·JZ_‹‘;³yłk®}Y’Ż·`”¬õ„_ćž]ßį­yt(&¦j1}ĆcL/ęoJMSļCĖ—æaĶĖŅ„BÜ{ī€{y@1šhńÜhRnąų¬ńœ;—‹‹MOwīNŽ(™ÉÄļŪMV®Žgˆ gėĪtéŽ ”āGžŽ™ß p5‚Ń^‡.'—õŪĻńYŒł.UܛrŁ9.‡³2¾¹ć/_„;ĆkGR’>§āN WŅ4ŠU¤ė¤„<[ūņӗw“OĢŁC«µs˜=}2otų„YƗ1sHv\Ū)Ā`4¢hy£PL ŖZ0b’zņdŖYā:cō$† ś]ßń|8& /%–ł/=Įļ’†Āfņ½”„B••ÆBõHĖūjšńüÕģOMcķ¶*“xė’pÜš!K×ĘÓĶš 1QŻPŅi¼|§>=*jѾZńɹ™YŲą²mģŒ 0™Jœd]Ń‹O@+eV“²Ļ¹JAw«ZŹ–Wg@1zóŪwU™õc±ń¹ø9ėhŃș¶MœŠ ¦TļŅʕ]ĖĆYś{ 'ĪXšpÓÓ»‹;¾™9œ7:JĻVF;)3få)ćuīļŖ.WŌ§‚{ƞDšĒ¬įci¾`õ]e°B!„B!„B”ę–vg婸Ų7φ†Å`ʦźČņ³«Š5—ŌŒ,tvrҳČ29`ˆhĪ”æāįćN%_ŅœÄ`2SøāŹĪąģ¼;hŖB-K²Bڟų/Čś›Ÿ–œ!ņ••ŒzŠóŅ‘©%°śõ‡˜³ąŽh÷…ūtģ±·ƒō”“BtśŹµØjšĘĮ£6*=QģČW'‚Z ā­VŃń<žĶ·ģx.Ц%¤y½®+Oʒ×Õ=øCjSŽ֗¦n Ø.»_żØ#X’%;·@ ¹bōf¹ņuik‚čNčĒß³ģĖslöjĖ€`?œŚEšŃŖoY ŪŽo§1Ō0R0µsŁuuUt¾T­ęJĪŚ?ŲžŃf’ސ·uėĻ`¬A PƶnjSĶ<­b«Wx h}•(Āģ?gėĘ8lõBó×YcŁŗå4öQ„žŻf›žŽĘRæV±1¼ŗ8ƒÅµ!™[xīaüĆ@ƒŽ/dńŁ÷™ųčŠė 1EćŠ1ß½ėNxHA®5ÄĢZ ®é¼2ŚI™1*GÆwšz=Gœ‚9|Ƕ䍃xńłJĢ›9€`y°Bˆ[ Y³flŲ°”Ōmš7o.B!„Bqǹ„ĄgS-%tėņ§µŌ4œ*…¢spĒd»€·£ŽōØčł5ś~ž“iцJÕŖbĶUIµŚØV½&\Ńõqåč Ó)©m!nƒō5óų-„/<֊—«*NŻ¢ųzü~OģÉC.;n ՈØaĒw?ēūzĻŖ'ɳ3ÖļBæGĒ3`źć¼bĪC +aŹ:Ilrezöj„}üJęoT ­Q ū¼l9” nžø)€¾¤4ŻÆkīÅćŚód;Vņ:S՚kÓX0q.>Ż#šŠŸādŚ„Ž4Å«v{Ÿē?īĻūŽoŅ©ŖĀńßmƒĘåȗS‘Bė+÷ąZļ3qņ)Bž’ƒPƒ„C7B?É×j5žy§NńŒ’źŖīÕDў&O ¦ĘĻcyķQƒśŻG į,;L`fl0½ß늻rmuÓ’±ńōŸŲ‡×“7č^ßcjOĒ®µņcŌNuF±ĻŚ—ī5Jšž¹„øÖ½ŗ(šj ē«.Lüš+¾xu&)Ŗ~µŚóĀ÷ļ1°…Ė5vŅ;ÓpÄ2¦{üI³†šü‡Y<«Óį6“š§ĪšŸ˜ī0œ‰Sžā©šŠčĄ³ßÄĄŗöoOäėK™į1‚OæĘóS°™Ż©X¹9ķC] ŚėÕÅY3{ī?ż±‰ĢłĻü­l€iB®l6°7ƒ‚Ū¹üæQU+i¦(,ž\QB}Yķ¤Lå)ć5īOD×W^āĻ×g0nvšæåx]Gš}Żį|4ģ/zŽ{ž÷’Åó5MrŽB!„B!„Bˆb(÷÷T0tG+ų§¦”i i*Zž/ؚЦę/«Ŗ MU󟁩ŖØŖō”D*?łM©;kUŻ™×;ś³FEC‡‚ІFnāYf¼ü$Y© ØŖ o;ģģøF4Ę/Ŗƒü©PїÅ?®ā‘'Ÿ¦< ·ņ ė§»NēīŹ«<Ørö/¬ÉĀzĮ¼Qéˆ{Ó ”‡čŌ©ó=U¦Ųó9R±wŪįxøŻ,’““įõdŽÜ;—†ĆŽį8Ÿ^ˆĮäTź–Ŗ5—Ls0é-£™\%tā¶2ļųÅßW -uŪšØę8»y”ÓéQt:t:]Į’zEÉ’]с¢ä/+ŗüĒR+ JĮŪ»ü÷xŠ"Ļ~BÓ4nŌē¶5«ßÕ±Ų±cŪ=÷¾Z!„’«V-'*Ŗž]]†Ö–Ļ{Bq‡»„#€’‰Ė$×¢b6ž{‚Wøų¼^Õ†ĶšCVf*Göļ¢r€?Ɵ&-#j¾žœˆOAwv =‚©Z·6šAĮšINĘLnč”KEŃŠPP žG©‘kÕŲz,³Ä|)f#Õ*qJø¾‹ĶJG!ŹfåšņéģT«äėŽ>õæOĀįĄĒYS:ļl Yu?ÄźYĒżc1ŁRŠķQtłcŃ5MEµęb±id÷#'āuŠĖ„ĒB!„B!„B!Źē–vgē©ü}(ö5ņG1©ŠŠjÉįŌńx–/]²e+ˆ?u†Ä³gł|ąż<\»Ńē³ÉĂæ£‘µĆq°7€ Éé©Ģžž9ž¾4lŃģŻŠō+(¬‰I#Ē¢–™?ךž,© Ö³‰<·0‰7gśµņ …Æ Sv6m<Ļ7‡ņČS 4jV‘§"ģ©hPIIIgŚāól+!Sš44!Ä ¦„qjū¾ły?§2Šżm֟‰ߤ®YĀsĒSņ‚ž¼J=0œ_‹įüzōY'A³¢š¼°y5Ąā×Ķ(£~…B!„B!„B\Ć­ŽįĀ­Ōųo/ĒćϰiĖfžųk)éi€‚¦i˜ Rs1ƒÆ“Š£ƒ=Įn(Ŗłx”h* 6`ŽÜÅXÅ”˜ ńÜYōš /oo«VÅ?07 ,Ś–T®¼å%gņ÷i+¶Ō\2 <Õµ"­ģĶ@ļļDēö¾¤$g±ƒ'/F9`ŗĮ/§l8›-$YJHG:…7ƒāAėQæŃz”„⮦3b­ŲkÅv !„B!„B!„7Ä-ļ>“jå”W¾&ö÷ÆņŸ[„Ó h<TĄāąš…É‰,$eęą`¶aÉĖBMŌ°«ąCNNN.v|7k1gΜĀŃĮ‘£qńX¬yhŖ‚‹‡;õ~ƒ³–*åŹ[ö©¦ü‘‰°ÆāK7…s»Ļóį¦lt“;9Ó8ÄČO zĄ’ĖŽ=)ü“dCōŤ#„B!„B!„B!„·Šįvģ4š¾ē9³…Ōų½ Śņ_Ō) åĻۜ“gįPš/üÕ\ ŖŠ½É³‰¤ƒŪŠÜüÉHĪįĒ%KŲMZff';āŽŸaĖö½(zP„^;Ģ9(ś«Ļ£³³;Ą)ҟ9‘—^ĻuŠc;‘Ä×1fU÷äķ¾īœ<œĄ‡æ„r\Ś“B!„B!„B!„ā6ŅŻ–LDõŸŒŁĶ ]ž ü;žWÓųfīOģI3’NŽŁ™$UGzžĘĮķ8–˜ĀĢ™ßó×ߛٲe¼+›—ĖĪŻ»ŃŠPU0;ūŲy4ŠŽXv†lVĄd§ĆTšRF– t$‘?œäNņĘ'ųpW.6[üOæļO1縊h­¢+6!Ä=LKåąŠ/Xøž4ź5ž}ōźÆY¼ń̵żżķ)4—³xÉ?$ތ)īµDö.żŠ%Ū’Šnvü“sl™9š/~=qŌĮMŽķ=wü©»;Ŗ.…B!„B!„āöŃŻ®›]*Pą˜Ż|ņ§‚ĪŸ MQȵZ™-™ęOeÅį\•›¾ķ Ū^Ȧcåč¾ĪųØ)ÄnYĒĮsŁ·ænvlļø¶tõW“īnv]–tø•ē"!„B!„B!„(Ćķܹ³o(Ķ^^ϙ/|lŠRš·  j‚ĆĒN ZĮ,Ń :EMC§SˆØQƒ_’ ƒ^kp"’“³Wł3’›Į÷kR©ŠĢ™Š'2/XpĢKaś’³d·š …ŸõQILĢ#8WZś+h¹VöķIäū£*Ų®LĒI‘F&Ä]G瀇 ž^NłwÉŲ¢łyģŽõ˜E­ Ē’th“älŲo&jX=ģ„„Hl’KnĒy č¹H!„B!„B!ŹĮp»3`vö¢Ńs³8ņūtāÖ~‡š—}YG°ŠR0:EAQ@W0ģFtF=Fƒ!=:BŚö#ä¾ēŠ®vfSŃēx5ś\į—ӘžSӋn~ų/>Wžt„w]’7…މ"T’7’ÉAs#^©ķ įŲ 9 !„B!„B!ī@†;!:ƒ‰j_"°iŽžńg¶/Į–›…¦Ó”iš¢CStč5 MSQ °wr%&«^]½‡æŌØw”ļ÷gĒ;z” ‘öŪ›<ū„‘ĪM÷³$öFokęłoGÓ¬č€=-™?OgīŠÄ&d”³÷¤b`8ķäĮx ÄŚz“ł“_zlÄĢčĆĆ3L4¾ŒaĶóĻIÖ¼ĻKæœ'1Ē€{å¦t8„œP¬;ų²’ėė1‹÷{śåĻdpn#žOŠčł ¬™’÷éŪædŌ†ĆĞĻĆŃ?Š6æČ#Mü(ń ęjū~œĘœ•›ˆK²bļĘÆN WøŌDö,šĘÜU›9–n!éŠļyŗEzę ŌŲüå,Žz”sŅČÅŸšš`S#~’“½q ä9R·ŪPž}ØVįY “DžYæūļPĖ@åōŖQŒ›½óéyč]©ŁįY=ÖŻeūŚǹÄTr±Ēćņųä`ć÷“YšēnĪf™ń®^S†VPö¶ņķÉųIOØĖßēńyyż—ŚŒų²Kéń‡²ćQŌµĘĒzŽķ ¦±xĶNāU\«5§ūĄi_ĶEĖ"ī—©ĢXō7± 9\ØßoCŚUD)1¶e“ŃŹIeĒöfµ…ŅŹzE<Ė*{õw5ū*Ós mÓX^’č0ĶĘ|Īö`gŁ[ƒYźń½Ž÷’Ī-ķŹØ’2ŽƒŅb¤½ņ\TŽz-­m!„B!„B!īi†;)3f— „÷x›Š_!įąZ’ŽüCŚéƒä^8-'PŠŪ;cöšĆÕÆUćÖ½IFK qg2Q5²&¦?›®ā¦`;¹ĆéV.Dǐ÷ f,=ڟ–Q «ÖŽųmĆÆ8Eä;ēuĘü˜C½>Cy#ĢķüŸ|’Łļ>ÆBHqeŃŅs/Žē‚‚Có„sW@+éVγ}ńē|÷ĮēųMF]cyb¤‘gq!²×0öR9¹ę{ęNޘ/yŖVqē;w8c¶ŅųŃaō©ę‚%9NJz —ó‡óĮRhžä[ō Ņ8žĒ7Ģż¹ć¦Ņ;ŌZ:'öķ!5dÆ ÅqÕ_}É×3C¹’ńA }Āž”­ß1cÖX…ϢĄBh ėŁķH½žuÉ/½‚[xś{OG »0óū÷ł®ņ\†6sBłw_AO3äłź˜rO±mń|÷)?>v™ģžśu&­q¢õćo3 ’ž¤ƒ«ų!†‚`3aQ‘˜Öīä@Ņćzé@K劁˜jJś:–­N ņ¹ĻčÕÜØ<ų;~˦żC‰4'“¦9S»V”Uģ€Š"ū+&¶åj£„Ä6äę“…2ĖZ;\«†VšƘVVŁK©æąŅ÷Uś‘eĖĶ,”³ZFz Üiōō‹42–/¦¤āøõužAS]©ē­Œ˜DÕ+½®jkeÅčń’öš[(.vŽv…Ī#B!„B!„Bˆ»ŸAB „ø™÷DUĀņ]ŃXWįĄž8Ŗö|»’dß)•PćNöŸ÷'Ŗ¾?zwĆMٶčT涓ŃÄåT ^Żą~ŌyūSA—JZ†v ųQ=̃¼]±œ±µ”J‘žŪ‰ār¼iXÓļŠNõä!Žå毻Xf}Xø-†Ó¶ÖT½bX”w/OČI!-·ąŖ ÷ĄĆdfp±ŚY¶¬Ę©įŌø8ņҹͳ˜½x 1'/ktĮ˜mCžWrń|š.ˆzś'-Ž4 ÷)qZZŵ M#'óÕę-¤Żß§ø=Ää„ұŽ )„Ęæ\ń(»Bʌv*–ųœl>}ˆ>“. ›UĮ”œ‰®MWŗ×YĒwo÷ćH«.txšAUq»TÅÄöZŚh”ŲŽ¤¶PzYSŲ9ķ)Ężž–ßnL­ymĪ“„—½“ś+#®„avMėĶ—åå•÷*–‘žŠ[Kžģ’/OXɹØ×˜Ü³Ģ)“Æ%Ÿ—ו¾~×rĒ茟õņżå]—×ަ‰Q®UB!„B!„BÜK¤Xqs)©Ū „9æmęhf&;śS÷ÉvŲķłžu»ĪŅRæ…xļĘ<ØæyŪ„ŚPŃ£×_ÅÓ0˹©¢Ó£GCUtčō`³YŃŹŸ𦁢+a{µ”Ž/kévÖ (šU-Č„bÄ W – özf-c]išX$’Nœ¬Ę/ąć@×įE^†«r‚_'¾ĖÖrĒG4ŠJɵāJ½Ö ™9éO¶„ÜGčīķ\jE ŗr¤mńøśųh ó ÅŠ ō¬¦/ŌhģÜ]PŒn<šĪl¢vżĀŖŸ2uč|V>>‘’WS ±½–6z#Ź~}eõĄ\c,£ļ³¼äF%ū`\J){Ye(5®œ+õķMõ^…óā§_YFz€–NܾĆä:8Ą”æŁzö:ųźŹ8”•ϤŅĖi¬\jūøĒų„ż]—JņNP!„B!„Bˆ{ŽNB „øŁ§æ¦- LÜĄŗŸ’fŸ[}jūzQ§~eā7­ą÷Ńx5nEeĆĶܶHŽ|ƒšÕ#&ś\9;S̘͐™‘‘?ės¹‹īŠ» $œ8‰„¼c9Āī½IŲWÅ·˜¾k_Uē9°ļ4¶¢ėB 6篻˜OŪIHĄRækžēUåģĘ5Ĺ6£IK]R–cŃ×jÓį±ŽŌŖB`•źų:—æĆņßüīŻµÄ­œv”©ÓnžśkŪž9N„&Ķńӕ?żņÄCÓ®½«XēW…JĘŽ¶įˆ’şJx:dT±§bŻīōw#ŗ»sxŁ b¬%ĒöźŪč­i „—US@-"jÕÉ’©Œ‹RZŁo@\‹©»üe劼øł—•žFś¶é|ł·;½>˜Ģ£Į˜;u)gÕŅĻåĶg©Ź£ėÆW„ų:B!„B]Ņ,Ó IDAT!„BÜSd܇ā¦Ółµ¦Yåo™»8æ‡&Ø×”kŌ‚J³¾b‰@÷”§;½Ūjiė˜6ōCbęĆAõ°wmI‡ß2~ŽfŚ=I“…s[’$Ž5‹+€¾•ƒM¬ų{«Ŗw'ˆ³¤ŗ6£YhY¤^“`-ś’éU,“q³GI+Ō‹¬‘}b»öäb—{†½«¾eé¹j<üJcģ‹Ė»Ks:w˜Ķ{óFš™ö­Ā<1dœ%§bk†4£[ē F-Ķt»“ ҈’c?ĘÓyp“žw[ź 6­‹Å­ń³„]6$ŃP?ķG~›’+-CpŃ'’YžīJÅ©9Ż;1jį&Ņūkś`ĢŽĒéœ"i˜#¹’>?Žüi<ē2‚č44°\w/)N切ĪgHŲ½†]§ˆŖxõįQ\šÓ¹żlĘü0šOõÓ¶†7ƜóO÷§MŪģĪnä×½Įޘ­ēŁ<œ]qRJŽ­rµmōZŹĪ/«C‘ĪDõL)eæŽ}­;ß"Ė~Ž…ģ–•ž}Övę}ńĪ=§Š¹Jt/<Éę”3˜ńkcž×±"JI灰2ņYÖįu1ŗYõ*„w£U«–K„B!„BˆH°āęÓłŃāž:,ˆM¦IóŖłø¾-iZķ+ŽŚ:Ņ:DÓ·Õ.Ÿ:Uq”žąqr8ęĢ•…Ę=:“NNžMo‡“‡?5ky“Ći®iiŃD'ŪįU-c·ŗ.µTā6oäHŗ{J„Õt'ķh i’‰ŲJ·†t !„B!„Bqļ‘ĄBˆ›źŁ_“ ÜåßĪæ$±B!„B!„B!®†NB „B!„B!„B!„÷éBˆŪF#ćąr/ł‡DM¢!„B!„B!„Bˆė'ĄBq3hYœ?ø‰=ń™”Ų·«%óĻü©¬8œ‹£"!B!„B!„B!Äõ“`!„ølŃüQŵZsŗ|‘öÕKĻ_513śšš ‡/cXĖ‚s––Č?ė÷cßąjÉš_!„B!„B!„BÜ w\°%/›ĶviŁb)“^UUTUÅjµ’““ƒ¹`<@Sˆ;S.±s^gĢ9Ōė3”7Ā<ŠĪ’É÷ŸżĪįó*Ŗ~S§oĀćŃ’ńn=/“”dzyѹÄĪĪ؟­4~t}Ŗ¹`INű¢Čåšģį|°š?ł}ƒ4Ž’ń sGæAīø©ō5–Ή}{H zš!ĻWĒ”{Šm‹æą»LųMF]sėķs8ųŻė|¼Ö‡īßēiÆ4ö,ü”oĘL£Āēƈr(-zBzŽćÅū¼QPpØ`¾-a=£©×³.fi$B!„B!„B!„øAīˆąģģlāŽÄrö̾żņóBės ??ÓŻ±p–ķB]ü½a£&Üß”z½žŖöŸ™™yńw‡×899]WY“’’ -§¤._HHp”eE¹¾Žm«ÕZhłņĪuø²ƒ½¬ņÅĘ)“\„Jåš_qŹÜÄŅeqų=4!½Ŗb“”S,W~@KK&Ms¦v­(B«Ų”—żķF–,‹#°÷7<ß3Ėl-cK–ĒŌūw DԌ ;ži–ž“‘®Ć[“4+ŲŌ”n0ōŌ%Āó »_ż±VźF”¾¾Nš:–­N ņ¹ĻčÕÜØ<ų;~˦żC‰ŖQrž(8ōLī• ņ+r‹ŠJ¦æ8ģŲ„žµd!ÄŻĮ¦j—]ļ%B!„B!„BÜ©n{š¹³g9°o/š¦]wZ’lŁDTżzøøø`g'sŖ q³éµŒc×v2šøœ Ō«\ģ GŽ•īuÖńŻŪż8ŅŖ |FUÜŠ¶1ÄåxÓ°¦EoėPOāXnžŗ‹3×Q…Ūb8mkMÕb:(t>xėRIĖ(>ߗÆWOÅŸ“M§ŃgŅæ[hŲ¬ ¦äL¬„äÆTŚY¶¬Ę©įԐįæBˆ»Dfžzńw{£N"„B!„B!Äź¶vkšĘž½{nxŗiiiŲl*ŽŽŽRĆ⦲ŁlW=āüž:čņ¬eܼ”ŚPŃ£×—0\ĢX™Ž™MŌ®_XõÓB¦ĻŹĒ'0ņ’Ŗ£G„äŌ5®å¶E§G†Ŗ–g½:Z @Ļj—׳‚» Ź µō<”PdõĢZ6ĘŗŅą±HLr !ī9—NœŽ ˆB!„B!„wØŪ6|CÓ“2ź·$™™WLß,DIN:Ōß0fĢū|ųįG,\øšŠé³‹¶ßٳg³wļŽ’tÜ̆²ēÕłį«;GLō¹’;K{*ÖķN’wg0¢»;‡—­ Ę :æŖĻs`ßilEÓ %؜æīb—„ķ$$`©Žß č—׳U”’1…c§mųāń§žŽŗRófĢfČĢČ p_³ŹŁkˆsmF“Ņż+„ø{$e^ŗ.{ÉōB!„B!„BÜ©nĖš„sē8“?æćlɌ©…Ö9+…»QšŌ (“\ŃŪ³ŠņŚ]» -ūN”åO'|†Ń`,5?111÷šš(“īųń慖‹>#øč²Ł\ų Ń¢ĻÄŻ¹sg”eOOÆBĖgΜ.“ģķķ]h911±Ōż»¹¹ާ³s‰eHII)“\źz®pƚ¦īŚ:{öl”e›­šśšš°BĖqqĒ -ŪŁŽ_ŃNŲśõėßšöh³ŁųᇨR„ ŻŗuÅf³‘PęČŽ¢ĻSž/r4ėIĻ)=ŠkK:“ų–ńóĘ0ÓīIš(œŪś'ń6Ø Øg6ņė^Ą`oĢÖóģ?žĪ®8) ø4§s‡Ł¼7oŸiOŠ*ĢCĘYr*¶¦aH3ŗubŌ‚ŃL·@« ų?fšc|07Įį”OqiNēö³óĆh>Õ?NŪŽsĪs<ݟ6m#p(-Į•ØlbÅßsXU½;Aœ%ÕµĶB“Ł“.·ĘĻ&żæBˆ»ČфÜKļĻ*;I@„B!„B!„øCŻņąœģlŲwĖö—››‹^§G§“gՉāåå呑‘Add$^^łņ>>>×ļŪ·U«V“››ƒÆÆ/=zōĄŻŻ€%K–°dÉ*1`ĄS’¹Ų9˜t(ŠRśh~ŅśƒĒń¬Ż4–ĶĶÆ&|C}Š+ :ŲR޲ł‡EĢ:“†Eļ„wµfōŚ›`=€ż'š†Ėt毚ČGßēbp ¤ń€z4©@hßyĆ<•¹‹Ēš^ ø†4¦ēČēéz£F¦9Ró©ī2…æMfüÜLpš&Øł ·Ą”Œü5é’2&~ł±ė°:ųS·o8ķײ)Ž“†#0Źį'„ø‹ÄžĻĄØWh\E:€…B!„B!„øSŻņąćqGoźŌĻÅÉĖĖĆĪĪNj[ĖĪĪŽ€€V®\IūöķÆĢ3Ļ DÆ×³téRÖ¬YC=čŅ„ µk×žĻŽ` SĄÅNOj¶µŌķūŖ“}n"mŸĖ_VOĢꍗVāęŖ` ėĖ;_ō-å,åMGFRē‘ā2ąEä#£ˆ|¤¤ Vžöī;Ίź|üųgęÖŻ½Ū{a–eéKļUQ!‚%Hl_cbŒFQ£I4‚-¶˜ü’h BDEAA¤w¤³lķżö;3æ?īĀ:Čó~é‹=węž9óœsļīÜēž9Lzł+&żXŲhśxō”BKŪėßkŹcōšrāķ3§\Ī3.ēŽ†Gtö滄‚˜aLė$é_!࣬6@^…€įŁįŲ-ŠE!„B!„BˆóŌYO—<ė'ŠhšÖā-}ÅÅIQ&OžĢ²e˘3g”””Œ=šöķŪąp™åŌ£GV®\Łš%UU1›ĶuübĀLŌx“&æŲńÕs©Ų½bbˆ ³£xĖČ]æ”ܰ®tßż9_å^DĮŅKXóÅ>BŚōc’Ā’QpԦ˯ų‰¼…ē­%;k1 °YTn'B!„B!„āč1µĘÜ ÕTŽw¤•ć­kn”ŌŻ»wgPyķŚÕ$&$ѹs—ŪŲx ą‚֛6Æ9ųąC¦N½¤¤$6mŚÄwß}GMM &“™˜˜hyē8zŒØ 1”&ŹĒŸ¼g×6ŹvlfēĮZ<¾†ÉŽ#&•nŻpīŪĮž‹(VFĶv¶WډėPKī®ąõø%,„8_9½:kö9ø¦W1af ŠB!„B!„ē±³ö žačšvĪN“ø¤ˆ¬¬,,«ōŗh’ÉdbĄ€lŚ“‰żū÷ĪēŸΤI“hß¾=[·neŊ@żĢaķŽéóIt˜OĄĄé=6wžņa ŠQī’!.0sÖUāöėōmĘĻÉģ_!„B!„B!ĪwgmįRż6S¦LirūĄ8pą1ĒĒĒs÷ŻrCß ń£@r¤…J—B„SC7 ŠB\€¶póŽź ~Ś7†ĘŹĢ_!„B!„B!. g-|>¬•ZUU%=.ÄYj&Ān¢Ā©QćŃ0$,„·Oē‹ļk0©šźĄ™%(B!„B!„B\`ĪŚ§zśyp h·Ē}ÜĒϊ=Zć­-‰ŒŒlv{xxų)Cjjź‰u°ŁÜlŁf³5ūüĘ3˜kéł-IJJ:„xŠó—IUˆ7ē0ćōé8½Ž€A@3df°Bœ'  Ą0ØpPT…_ŽJ”Ū= !„B!„Bq“iBˆ3JQĄaSqŲT †B!„B!„B!ÄvÖĄf«]×é2ĄōxÅŽø ²³¼&؜_œ4š·)x įˆŲą5ećĮ·šv„™qŪx}Y!„B!„B!„B!„ų19kSņTÕtĪOöToæ,„B!„B!„B!„ē³³–n¼öģ¹ó£č4]×¹ńʛxłå—e !„B!„B!„B!œµ°Åj9ē'÷ćč4UåŖ«~Ā€Ok½=ö©©i$'§Š±c'®»n2+W®”W‰B!„B!„B!„ˆ³6-×nĮf³ćUF=nU·•Ø**k1éAåøšŲązĆ"‚Ź^ ØÜ»wļ†Ÿ“““47yņäÓ^gMM-7Üp=>ś(555̚5››o¾…;wČ+E!„B!„B!„Bˆ €z±œhxxx³·”ž1c½zõ¦M›tŗuėĪ+Ƽ‚ax<~ų²³;ҵk7f̘¦iAĻ’ōÓł <æßĄ+ƼĀŌ©S¶’üēæą…^l±¾W_}•>}ś’–Ö†«Æ¾MӎūŲ“iÓųĒ?ž €ĖåāĮ¤K—®ōčѓ’ž÷ķ†ćΜ9“ž={ѦM:'N"8’ Œs3f<ך˜Ķf'22’6mŚšĖ_ރÅb‘W‰B!„B!„B!„ˆ³ŗ0Æ¢(ēģD£££šŻžĆ?0qāD¦M›Ź–-[øė®»>|8999<ūģtÖ­[ĒܹŸąńxøė®»‰‹‹ćÖ[omx~ß¾}())”¤¤„””Ö®]Ē÷ßߐŽŗu+Ó¦Õ'„›«ożśõ\{ķµÜuםøŻnL&Óq;Ś_’śW¶mū/¾ųœ~ų_žņ^ čOJJ O>łÆæžżśõ£øø8( ®Ŗ*}śō!+«ż1ńšz½Ģš5§Ó)Æ!„B!„ų0jóŁøy'ū]‰Œø¬ŃŹÉķ#„B!„āüvÖgwķŁ’¬Ÿdxx8”””-īORR£F¢k×.l޼æßĻĒ̃>@ēĪéÕ«wß}7³gĻA×õ ēfee±vķZü~?;wīÄjµ’››K^^>N§“=z“Ŗ¾„„bccIKKkØ’xųż~ęĶ›Ē}÷żšōōtFMÆ^½X±b&“ «ÕŹŽ;±Ūķdeew¾Ŗņ—æü™k®¹¦į±·Žz‹ĢĢö“k—É‹/¾Č3Ļ<-Æ!„B!„ųšm|—?<ż7žńæ­T'æB!„Bˆó›łlpŲ%£Éés$ üĒéOmĻ÷–•Ó-!AeM ¾ q•Ē^90ØÜ©SĒn£ŖŖDFFįr¹šz½ŌŌŌ%^Ū“iCYY𦔪õ9t“ÉİaĆXŗt)dee‘––ĘŠ+Q…~żś‚Óél¶¾åõz©ØØäÖ[ok˜am$44”żė_<ńÄüćopß}æįg?»©Ł™Ų×^{-÷ß’"""ˆŽŽ–WˆB!„āüb³ą¹§ywsUµ.<š‚-,†”Ģ. ¹r“†¤aæĀPł ¼ė%–×)Ä_ń’ļž^„œķ68÷³äć9|ŗl3»‹kŃ,‘$µĻaųøŸ2qpBdö°B!„Bœg=l2™ uąt՝ׁ9œ$µŁlDDDPPP@§NČĻĻ'66¶!ł{Ųčїsソ"))™AƒѾ}&ļæ’>ŗn0vģ(ŠrBõµ†Ķf#**Šæ’żu tĢöžżūńŁgóY“hwŽy=z䊣G&ė‹ˆˆ ##C^B!„Bˆó“į”4/ƒ:ęŠb"T|uåģŪ“˜½›7‘÷ą‹<2,ŠwīQcßēsYķ4ƒ²ÅsłzJOĘŝ½³6*ךņļ§3?ĻKĆד}åģßü ooYĮņ«ę™i=‰”$°B!„BœuęsqP›Ķ†®ėø=®3zœģģö§\‡ÅbįŚkÆįŁg§“––†Ūķęµ×^ćöŪqĢZ¼={öąĶ7ßdΜŁ$''ó»ßżŸĻĻōéĻžp}­mßŲ±cłė_’ʟžōg(,,$''Ć0Ųøq#:u"33“°°° 5}u]ēxŒ~żśŻZ!„B!.„ĖٜŪ^āé±Ń(2¾šžf,«bõņmų† ʦ`ɛ3łtżNöUąŌ¬DµĒļ§ßD7µ’Msß濟­fg™ŸÄŽ ½ś&n»"›pŖYšĒ_šüZ?I×ü…ż¼3ź¹ļī·Čķ÷ŽżżpœKxā–XīKb⳯r{'(Yó>ƽ³ˆū«ŠģÄ·ŹķNc`¤Z>~‹·>_Ćī Grg†Mø…©£ŚÕĻ’m®­Ē»j÷lä“/rŃĢķéŪ„œu›63÷‹½Œ¹©}ŠE¾įÜŧ’śsVģ¢ÄFj¼A€ąµ Z³Ļ1Œ¾ūÆ2?Ļ aøźŽŪ™Ō?Ś},żą ŽX“Ÿ=ó^åĶ>/ń«^” åņéK’ęó…”VÕRēUp$e3ųŚ[¹}t{B­ł !„B!„8+ęs$$äĢŽœ*;»=mŪ¦Ÿ–ŗxą\.7W]u5f³™[o½…›o¾ł˜ż, W]u .$++ UUéßš¦wĀõµÖžš(O<ń'NÄétŅ„KęĶ›Kuu5æžõ}ģŻ»‡ĆĮ„ čß’Čķ·u]gķŚµr«g!„B!ÄLĒ[[I„3(DĒÅ`Š Yµ`›\`‰$Ņę”ĪJ”ābÓæ’Ą#sóńc"$ŌJ]į&>}m»kžę¹ÉķčŁ; óŗ­”īŽC¹Ń‰Š?«xwļ`O`8]öķd·Ļ@‰Ź”o¦ £b!/?;‹UI餌\””ū±†)€›Ķo>Ī£Ÿä°Dg„ŗ`#s_Ź„Źś FiŖ­ĒĶĀT.’‚„•`ļ=–{'åń»-óČ[ø€ļ¦ļįū_å,|įI^YYŖČ(¢µč•ÜmĶ>ĒkAå 毨ĀPlōøéžļŅÄś׎®Œ»ē7Tīū oļ)eń—ųEÆ!„ź„l[µ™].°†F⤲p3Ÿ½ś4jĀ+ÜŪĖŽQńM31B!„Bq"Ģēņą!!!Ģųć <OĆckæż6hŸåUAåPGDP9§G· rūöķNŖ-’ś×æ‚Źo½õfP;Ÿyęižyęéėyųį‡xųį‡Źo¼ń÷ćžwSõ5nGk cśōéLŸ>=hŸ„„–.ż¶éĪ7›Y°ą‹†ņ‹/¾ Æ!„B!ÄĀĻśWoeĢ«‡Ė ęäŃÜ}]Ēą ]%‘«’ųwtV tL• xł³üJ#ļ†GDRōéÓüźõģųä#VŽ’-Czõ¦i+»ömg§g4ö-;š`TžĄÖbwģ¤BWļчĪVŠĖrŠg ŲŗsėÓ`|œ # a˜Į([Ā»Ÿå°tā¶ŸbJŗ™ŅāƬgł+(v%qM“Õ|¼,¬^Ä×_lÄECG $±s&—¤ĶēŻüņFśŒŒ¤~Rń·üom-†)…Ÿ<1{z9p~ż'¦<æżpU­Ųēx“y 0µ”OÆųąŪm›ŪŠ;'wöÄ[X@±ķŽ:æŸ<ńwd—3ļ±_ņź¦2V®ÜÉŻ½rPš‰”B!„Bˆ£žėX,ĀĀĀ0›OżŖĪį“B!„Bˆ‹‚‚#¹]»t¢cŪf-䳗¾¦Č8žžf³‰ĄŽģņ(½3$3VŅ.N7‹‚įÜĆö 5„żÓLī]lŪ³‡ĶŪjQĆĀŃņłžQ7Ä> IDAT‡"¶mĖCSBčŁæ!€)½R,ąŁČĖwÜĮ}ĻĄŅB/ Ų»ƒ>Ć·ß=‰ŃćÆåʗ×į2 “Ņ"J“¦ŪzŖŹ«ń j3o»ęĢŽt°,fCĶz,+„Lj(оł–-~%,“ģ“śėĖöƒüŃĒģłf1(éÜ4ųwČwk¾f¹öƒč~(ķØ£ZĶdĢm3ęś\fžž7¼µ#—ėrƈv“5-a[ ü0®›AØžźŖ­ńĝČWļ÷,\ZŒ®˜pÄ%e;¼AĒUVD…g‹ļcŅĻŚcĪȤy1ß׬ežā"z^~lF¼å}, ŗ’=Üß8Ž•Œéū.—װ靼~ū¦`«ĖeéūÆóĮ^ CeŲØ^„¶öܚ‰įґߤB!„BŃzēåj:V«•Ääd““ÉīŚUzIˆóX…3Ą»«ŹY“­ŸfH@„½:ÆFWc_™—E?Ō`5)\Ž5’)żcˆ “… …āō °å­ū¹y–‚ßYM„Ėa(„ē\ĘŠdõø³N”ŲL¹ālł_!‹gÜĮŖ×­ųnŲČśÉ5 <“²¹ż`†&Ļåż SZoś§G?°3ÖµšJļ”}ˆ8œ’Ż=›ūśw|"q”~Jöė „’”…št S.łŒ'³üõ_1é­ĀµN—=žoīļŪśŁ­® ‹YVi Ųūp׋pyäįģ±ĘŽ’ü’_Ļ)$’Ū%ģø¾=]/aŹ„óyāĖ"–’ķ’øīæQŲ|UųĆ_”VZ±ĻńƒĶČŪļ`ķ¾YtpM’ m·’>ę.¦ö kõ-œ›‹”ÜZ!„B!NŒ|‰VqR Ž_]Įķoåņł–jIž !D|šĮüĶUÜžV.³ÖV ļ–BqŠq©)Ú8Ė))­ F %1³cnś/>r)Ķ]é*”ōšöOżl8ģ<”I]óó'ųĖ h˜TkjĻ„#30+* }ūŅŽ¤Żoݬ Jä.ļѐ˜Ōt©mųĖóŁ[†בK~ö;žoxŠÉĄ’ū3OŻ| ŻS#0yk©õY‰k—E‚Åßśß F«æYK”`ļ1„žG§EMd Lš ōāå,Śź%œžwż‰?Ž4‚nI”j+Ø Ų‰Mė@ÆõÉėÖģÓŌ‡ qCyąłgųķÄ”tK‹"ÄbĀMz·įÜp’³¼xw?¢O sŪl eŌ !„B!ĉ]:_>éŽCכʔ’ 0  CĒØ’ŻŠ1ōś²®kŗŽ®ė‡žÕØ­*cńē³%¢B4ażśµŒ;žGq.næĪs ŠX±§N:V!NŠą,æDȏü¶Š{%<*U5”Ø*ŖŖś×„¢(õ?+*(J}YQQźĖ(ŌŚ_’‘æ¢ČG’BĘį4”\· !„BˆskäŲ‰r½'„ē9™,„8!æĪļfåKņW!NŅņŻu<<§o@ę !„B!„B!N?I !ZͦQĞRÆC!NĮ®b3”ŪA !„B!„B!N;I !ZķĆ5¬Ü+3…ātX¾»ŽYk*$B!„B!„BˆÓŹ,!B“F…3Ą‡’Øā¤(čt`#ŁĘZāMQź@ {éĪVe0^B$H©×T0ŗk$Q”& †B!„B!„ⓐ°¢UŽ]UŽĒÆK D#&ŗęDÓĆ]Ķū»üühFˆb¦G(²ŖŖų(7pJ·éķm|Šѧ]f–ųpŌ †_'PńåyĻ2·pŸ1 Ė…wŽŠ™AƒćéSUĪk[}'>3ƒ‡&2¤¦”ē6łøŲŽeÜ~wW•s÷% ņv"„B!„B!„8-äŠBˆ¹ż:‹¶Õœ‘· Ää0zĘŖ(ēućo;7ßŲž7†…bm.®Š™œnьH0½xÓ¶3@±ŠæW4CćN~)†Įuźó<Ņ{FڰeÄ¢†ZAQ@QP¬&,I‘$õeŚšĻųżnģ8Ļ^Ÿ†s<<²3t‰Pźė9įž1‘Ń&”,ĒÅūšżj[µ|ĮF!„B!„BqŚHXŃ¢õū]ų“ÖĻTB#xōĪlfOˆ"”¹ŒŽÉĪOĒ„š³ŒÓxėÓ3QēÅČŠ)ÆņQP§ÕĻČ<qU¬L¾¾ļŽ ktū …öƒŚ2wjŻÕ“hŪyźå&÷ł {ŪXZŹlšcĆ0¼‚;¬æG1Œ×Ų-ņ 6ä¹$B!„B!„BˆÓBn-„hŃŖ½u'“z×(śh\)QŒOŖęß ā…Ęš1~>ó„m'ŽŅö“nSóķMķžĀcCI“+<>6_Ę«+”n²b¢[ÆxnĪ £CøŠįÓ8XīfĮ·EĢ+i)VGāÜ{T;OŖę¾÷ŹŁ«¦0īšBņŖ}<²¹~ ؈.©¼9BēÅ’T:!‘ūó¹{™­©øīŖÆ=®cotµo5Ø(­ć“%%Ģ+ŅOn]ŁĆżÓÖJRø ;åGשظžśFmSĢäōŽć–™aą©ó2oAļ4@1Ó³o<·t£](T–:łly)sņGfؚ¬ Ļ BI±źyńŪĄt»Zēzzu!…5&™Äæ’ż/œĪ#³;ķ6•p‡Jiy€1cĘn7X°ąĖ†ķį‘ߣš] øŁqŅRĢZ{Ž­=^³ż×ØZÓ¶ §‡pó„T.w–ņŠüj “‹ć}vw‰W~Ł!„B!„B!N I !ZTZėoõ¾‘™Q £–gwxŲįŖcŹ‘ Æe~“`ƒ½ėšü¶:®ZPé:$•‡³żĢžöÆ×ščÕ/ŪĒĒSüv1ė#¢¹od嫊x(7€j%¬.pT"éxuĶDzjŃ„e<󕇀ŻĪųįqÜ5ÄĖ+J™¾Ā Ŗm wNbJQ.’,<µ6©qĶ··ź`5’YPI¹bÓ¢¹}PÓJ÷1}·ŽBö€4žģ£°zU Oi(įįL½,œŽį@IóķZė;“ł|ģdŪaÆ LŃv:Śb“ķX7×įE!+ŁŽR\ĪV¤¶ŖÆźy+똹ŃE¹a¦_ßx¦‹§ąķbÖłNfÄźŸņ2f|ķÅg±ŠæOܑ:Ž ŁRy¼,_UĢŪÅ–0ĪjPč8(•Ē{ĄāåEü§2:ĒrĖU©ŲfēńNq}ßöžŹo;ź,Zq7* bS"™œttr“µqEQ=0…uė=,^¼8(ł šŌż)ŒIēQ[‰ŠŠbÖ¬YAŪµ7°Äµ9&1Śü8i!f¾Öœć‰ļĢō§b²rŘdĘéU<±ąāIžÖæĻ䗍B!„B!„ⓐ°¢E.o+WņT,ŒģJÕö6ū!°æšÅĪ4ĘvµńÅJ/Mår|N¹åž†¤’bwpM73ėæÉē½]°g±™>·Ä24„„čl(p³½T¼-ÖyÜóŖr³±Šƒ†›ŅØpśõń²āū:ÖkĄčÖ)…®)ŌBĘ)“Iµ7ß^g™“•‡~ŽU¢‘‘ĮeÉVL»=l®ķi„p]3Öz J؅kŒšVÅjm ]ģ$–nÉ*_ģщI !ŃÆa$‡Š^­c6rRUö’ą¢ŅPZÕW‡×¶­-©ćŪ}õ³q·ŗ¬ō¹.‚~ń ė Ožöß®J7ėņėėÜRg”×į:4ŚŃę`BO+ū×ģēÅõ¾ q¦ŲLȱ²oĶ~^ŽäC6š›Į„Ž>ž¼WˆƒńĶģX‘ĖĖ›[”Nf—0ŗ“rLg€n1„¼õż÷ǾD54œ¬L©V>ųąƒcöɊqQLn"Z?NZˆŁśŠ–ϱ±Öļ“ö§jfČØx¦E:™>§ŒmŁ„X§O“_6B!„B!„BˆÓBĄBˆµ6…gŠąņD Jinžąe|—:­)ek+ó¦(m-*‰—eņńeG=n_˜Š¶½šŁł~~mvT3s5+J5N%}RYĄ°š‰4`(wA7›ŠØ§Š¦ĄĮꌫ”Ėm}tŠ6cÓ4üŠŖõõGŪho°:Ļwܤ[K±RŠśĻp9YSĻ5mģXöxéÖĘʎõ•x{‡“ Ūµŗ‡ūX›ėGĒzұŌk|” a?}cPÆńQÜDęh;ķĶVų¦h;™‡¶5|A÷³å@€ŚŚHUkŁe£źgÅĮ@“cżDā 0 ‹…~=ĀX³ÉŁš˜¢Ą=·$гK(/ż± ×ß³·ēČ,ĀĆLÜzUĻó‹ęĒIK13µāOõx§ŚŸń]“øĻģg·%¬r]„ļ³²TŗB!„B!„ā49/Ą†įĘćYĻ·æ?šVŠ®×ˆ®Ŗa˜LńX,m±Z»b·÷FQģŅ“Bœs ŗDa¶pŪĶŁÜō¢6sY›r¶ęg&±qÜŖĄ°xa!ļąqišĘ’ęę².=‚q½£łõõŃ\µ¢G×xš5Ug Zżķ‚U„įˆ€^Ÿ“SNµMš·ÉmŲh…¾µ„–x©4,ŒĢĄĆ”PT 4½É°7ß® XūY—ėå–.a“·©ōIö³~Y īŌF¦[ųF£m­“7šZ䵕q5 ŠŽåQų5°Z§ķV4£ÉŪ7]' ³Ož—N}%ŠŅģšn}œj,_½•Å?Ŗ`÷~/Qį*Ć„sé †Væ÷O.‰d槝™·°Šüƒ~b¢LLłI4‘•.܅Ž śŌĘI‹1kĶ9žāńNģ÷ü±żYs –Ż1üä’XÖRĘVYW!„B!„B!NŹy•ātĪĆķ^†aįH]ÆA×kšū÷ąr-BQ¬„„ !,ģjĢę$éQ!ĪK(£²ĶģX]Ąė»Nˆ™zi*Wv ćß¹µ-lčxjSQ”aö¦Vå%_‹"3 жūšøŻ¬Ī¼*ž™Wòaé<Ż#’.ė=ll¢ĪSuJmҚ޶=ĪN†āęՕ5lōŠĘĻQĒ­öqĄˆ¢s²„čŲ[Z·®]Gī®c’€Fö2‘ćrņQu×~/·µdŒn§lO{uŽMŖžŽøņ*t¬éatµÖ±įšŪ¼ÉFŸ43ž /wģVōMžE×4 ¦¢ą[@k•öź·©Eõ·€FµŠ-Ō·ĢK”~dŸéVĢ½Ēį‰Ęy‡Æ'£ßs÷ĘśS7ąŠ{\üķm'i‰*&ŹŖ vęj¼õd4Ū™8¼ć’]ŁRäÖĘI‹1jÅ9žźńåŌ2ńŽņjž]ģ澉‰<2ĘĻŸVs@—·V!„B!„B!„8QźłŠĆšRSó„„æÅåś¦ÉäļńŸėĆåś†ŅŅū©©™‰aų„W…8ĀŅĆds³ä{{J½ģmųßŗ»<Ų3"ŚčIŗŸ=e)cŸJĻ̆'›Ą]Ē'[ż¤õIꁞōK ”g»®źl'P#Ć×ÕA·DY)”tQĮ£Qk4]ē)O=…65·Ķ_įåv®čAÆ$™ń6mG×UĒü]ś's{·0ŗ¦„qiÆpŚŖ­kWczUߖXŪ7œŗ½uģ×”xoū£łiŠŸļv6‘<-q5Ų“©Š½ö~{uWwt0 C$·ŽOfB“Ÿ…ėØ9…¾ÉģŸĀż}Ćé›BĻö ŒS0¼u|“ŁG»~Éü²GŻÓüdT2×Åś˜·¾'`x똳ÉGZß~7 ‚>i!äd„ji}’7¶Š+ØÜćBÆš£Wų1*żüķN3q!K×Xø"@u„Ęć7šé©7ģēŻ[Ī׎k©Æ„qŅbŒZqŽ'}–-u±Ŗ,Ąö…<ˆēę>É< U„N>œWŹģ¢†Õ‘ٱ²€Ē<ńܔĻ£żMؚFY•›å‡Ūtbqv*Q¼_q+Óųf[}Š8Ó ÆŻŽh„Ų@!€V|č,ō+K;³‘KŽ~‹ć¤%­9Ē“<žįgńźJ\É“nulXvjÆ4OQĻÆ å…A‰üt_>ļ•ŹāøB!NŸoŠż¼¹ĒƖ* W@~Ljs'Ō¬Š=ŹÄmķķŒL“œµćz} ¾'Čf5K„BqĮQ.ŸtĒ”«^抆†”cŌ’€nčz}Y×5 ]G×õC’jŌV•±ųóŁ'tpæO£ė5§õ¤L¦bbĀlNoÕžÕÕÕtļžƒĻwģĢc‹ÅĀźÕ«q¹œ¼šĀ |óĶbjjjHOOgŚ“©ÜvŪm(ŠĀž={Ni{~~>ƒĮļžĮü—æü™©S§¢ė:’üē’ćĶ7ߤ  €ÄÄD®»næžõƱZ/®9R ,`ÕŖÕ€ŻBZZ*ƒ¦mŪ¶§żXĖ—/gÓ¦ĶÜqĒķØjż”K§ÓÉ /¼ˆ¦Õ_8›Ķf~’ūß·Xלõk;vüóq/ķ”wĖóˆĆ 7D²yv.’*’.ĻŪ_°ÜØ<Ķ8ĒX¬Žf÷Õ^¾ÆMf†ž."$x©łæŹžQOēŽC ŠCUM(ŖŠŖŖ‡ž5”(JżĻŠ ŠR_VŌśuŖ…£`?õ[Œ ńc`G¾øt¶ÆŪ~,^Üīęļ;=2˜ÄyēĪl;æīrVŽUSē%6F¾0/Dk•WTį°I „hdäŲ‰r½'„ē¹sö¶@ čŒ$4­‚ŠŠgˆ‹ūK«f+ŠB›6ix½GĄø\.BCC1 ėÆæ¼¼<, 6›={öšč£ !!‘Q£.=„ķćĒ£¶¶Ć0 !%%„”‘‘õķķµ×ųóŸ’‚Åb”S§NģŲ±ƒ^xĒĖcżį¢“‡^½z2jŌ(\.Ū·oē½÷ŽgÜø+ÉÉÉ9=ą——óå—_’——OxxxŠ6ŸĻGxx8wŻugż7ņGŠ8£2²¢č¤ų8P§”Ūl ģMzu5ÆĖĢČóšĀLżav×ę0ÅņI¶:L–µ~½_ĆŠŃ^ź¼Ÿy'2Ļøæ",!„gĀābæ$Åyėļ;=ōˆ6sÉYš )A¢•Ź+Ŗ$B!„ø “°ax©¬|īŒ$«OĻ 6ö „łÓŒˆˆ`ٲe åƒrłå£ńx<<žųc¤¤¤šųćsą@!7Żt&“‰É“§°bÅ ¾ūī;ʏwŹŪ'€ ČĢ™3ƒ’Šš¦1wī\¦O–É“'óį‡ņ«_żšÅ‹_t `؟™Jhh(C‡Åf³±pįB:uź„ÕjÅļ÷³`Į~ųįTÕÄȑ#čÓ§@€… ²iÓft]gųša 2$Ø~§ÓIFF=zō`ɒ%AŪ¼^/!!vĢfsƬ`!Ī•„d×f[‰QQżr Ŗyva9Ū5‰ĪłĪPV2ž5ž+čä_E'Ö§ĄFµĶ%‡ĶŹ\„ƒ|—D!„8cž³G’æāüöęĻYK !„B!~üĪIø¶vV«×ü-)ń³iS%%õ³s¬ōģé >¾å #æ7uu>鄦÷Ā /PVVĘĄ˜|št€B!„ē1gąĀųĘUFh>×·ł›ź•N“1*„B!„'ķ¬Ļv:ē-³ÕēÓY¾¼†C“eƒč:,[VCr²«µł¶ax©­Cdä“Vµo֬ٸŻn čĻĄƒ¶išĘSO=Å?žńObccłĻžŻ0K÷T·7ŽnŻŗSZZʚ5«yē™Ģš5›6mŚšĄPQQ×ė%11‘ŒŒtÖ¬YKnn.sę|ĝwŽqŃdæßĻē#$$MÓp»]‡³Ź”q`‘‘¦ixŹ “päSżń ާOU9Æmõó>8£±ż±½~÷Ży֗B!Äé2:n1“Ū®&&ÄĮŌĢyģu¦“ėJ—Ą!„B!„8agõŠĻz £u·²r:µÓ²ŌĻöx6¶ø_^^;wīÄb±0jŌ„AŪfĻžĶĢ™ļ’˜˜Č¼”¼=Ūsss @żlÖ­[·ŽĖ墓“EQ°ŪėŽYYYØŖŠ×{qŽĢēóQWWGii)K—.eįĀE\vŁ(l6&“‰Ī;ńõ×ßPTT„Óé䥁†Ń°ķ›oź·ÕÖÖRVVvBĒŽ»w/eeeTWW³råJÜn—¼“\°ļ€vn¾±=o Åzč-119Œž±*A«:+frŗE3"ĮÄI­ö¬˜é‘Ķ%‰'łüsĀĢ žń\• Pw&”Š…ž½¢§¶“S’”:²3t‰P΃>8Ʊ=ߜ†×OPߝW})„B“āzTWŠ ŗß„ę7£ł­h šfF7T T"LÕLLū’˜3ŠšĄč”ܓõń¶²£/l%˜B!„B!Zå¬ĪöŠLū IDATł¾oõ¾aa¦Ó²ĻŃĒ Ōģ>6l M|||Ććŗ®3sęLt]ĒårqĆ 76lKLL`Ö¬Y§“żż÷ßēŗė&SRRBtt45558NĢf3?ūŁMDGG3tč¾žś®ŗźj²²²Ų·o~柑#G\tƒÖn·³jÕj6l؈Żn'55…É“Æ#33³aŸĖ.»Œ/æü’’ž÷æų|>¹ķ¶Ū0›Ķ\~łå,X°€·Žz‹@@£Wƞ\y啭:¶aģŲ±ƒ-[¶ąółHHHdāĉņNr”2tŹ«|Ōiõ³Mv~:.…öėsŁT~qĻ+TĀĀ–j°ö n)[!„Bł]®ė؆ŽfjśrŚšh.šĒŠę³¢ė6t“Lf0©`ÕĮŖ£X(&#āW04a+ŠŚ“93·įÖßē¹7Qē £ŪŲHW4Å, „B!„¢YgõŹŃļĻoõ¾iiõėū6uh›M%-ĶzĒĪkqŸ;vbķŚµĆl>MÓ(-­’ęuMM 555 Ū|>ß)o÷ūżōģŁ“µk×RZZŠĶfcŠ AÜsĻ’1jŌ(^żļ<żōÓ|śé§lŪ¶ŲŲX&Lø–Gy䢓cƌa̘1ĶīcµZ?~<ćĒ?f›Åbir[c:t C‡ eEQ;v,cĒŽ•wĆĒüłłĢ—H#&3œ.~'Ķ—ģJl…BqäļGƒ+·­#?:ŽĶ©ķŽŻīŌŠK ü•f¼n;ŗ9l°[ĄjÕ (€ŠŖŁ¢0"a‘v°ŗšP±ZR˜Öa3±xG6æšĻäi~ÉfŗH?ˆ³®¼¼¼Õw 3NÓlõS©'--M:M!„B\ŌĪjXӊ[½ÆÕŖ2dHK–T”7śŒ\Uašą¬VõŽ]Ōā>?ü?üŠ1[,–/_ÖģsOuū?žńF³Ū###xꙧyꙧeԊóBļQķx<©šūŽ+gÆ˜Āøwj É«öńČęś5^#ŗ¤ņę¹»C˜pś÷}ńßł¶ńgЉn½ā¹9'Œį*†Oć`¹›ß1ÆŌŹõ×§3r>w/ó :—NĆŚńé0ƒeŸļę/»ź«Šė˜Ä]-Ä[ *JėųdI óŠt S(wߖJūõ¹üv½P"¢łėĶŃä~²× ½Ž3āx:ĖF‡pg•›E+Jxg“a5“Ó;Ž[rd†§Ī˼¼wŠÅLϾńÜŅ=Œv”PYźä³å„ĢÉŌĻfVĢ žÄäv6’*6tŠ j˜»Ē [ēpzʛ±x}¬ŪPĀ+ėÜŌ6:īĄ!øs°łPćR»§šŲĄPķ ĶߗńźJ'åĘQĒjk%)܄ƒņ£ć`²2lp<7t %ÅŖSTäÅoćŠ¹+ō»<“G㫸÷½röśœ'£/usó‡·Ŗ›’”67ćÄö¤ā£Zč×?Ž)Ciė€ź's¾-į³b•ĢnńÜŁ×AvøJĄķgÕ²Bfüą?²ĘńqbŪü5µŪ35š=×ĘZ>÷fūļ„Ž%„Bœ;½ņ÷qĖźÅĢź5(8ģ`ĪwĆ æÓŠaµ`ŲM`QĄ¤€ŖŌ_Č* ؘōC’DŲŖčµ; ŻźŁfŽį61Ā“™ģż+ŁķmĒvw>¬Ņāģ^ń) Ŗzä3˜ę’³Šrr u4®ódėB!„Bœå°aœŲĶ.32ģŒ˦Mu””ųHH°Ņ³§ƒø8Ė Ū#½-ę{u³#߃Ɠl;ģu)ŚNG»Bl²ėę:¼(d%ŪQŠĖł:Oa|öéßw«ļ˜ČĘ“}VÆ*į©" %<œ©—…Ó1(=ž¹ģ]€ē·Š1pÕłĀ[YĒĢ.Ź 3żśĘ3m\<o³®uĖ‘c5ilX[Ģūµ m:Åš³±©X>ÉćĒKQ*dHåń^°|U1okXĀL8« @”ć Tļ‹—ńŸrČčĖ-W„b›Ē;Å`"=5„čŅ2žłŹCĄngüš8īāå‹„L_aÕ6†;'1„(—9OÅį`X²Ęšõ.æsV¬ę? *)÷BlZ4·JbZé>¦ļÖ1«¼Œ_{ńY,ōļw$>>•ŽĆSłmGE+ņF…AlJ$““'€ ¶å¹ńu ”[ģÆ0Ń)Ŋļ@9»učÖ\ü}­‰Gc'•®CRy8ŪĻģošz­‰^żø}|<Åo³>"šūF†Q¾Ŗˆ‡r(”VĀźA ĢccŪŅm)¶gj,4®k½ÖŌø–ϽéžkįXy‡Bq~õzøvŻJ:å3d_$‹:t§Ā‰„°ŪĪ”b Æ9ģv EEQ!ŚRIŖ­ ‹]g·Ś‰5ŗ>¬rč_d{1ķBóP”Ø&ž† ”k{JH?-XĄŹŠŽĢw]&"„B!„¢IēżāAńń.»,ZzJˆcčz€Ņ¢B6o^G]u9=ŗu"1„=”QÉŌYZ9ō’™ē*t±“Xŗ%«|±G'&5„D憑B{µŽmŲČIUŁ’ƒ‹œ‘}+ēłl®ķi„p]3Öz J؅kŒšfĻÅēō‘[~ō¬ĶśjKźųv_żLį­.+}®‹ _¼Āŗ¢ÖÅØto%n­žś|/Öč &å„1³ 'Ƕ}BO+ū×ģēÅõ>ŽĪ1+6r¬ģ[³Ÿ—7łŠĶ|„Ęf0”·ƒ??RŸ«ŹĶĘBnJ£Āé×ĒˊļėXÆ [§ŗ¦XP } 3eڇÓŃėäƂ#u–9Yyčē]%Ł\–lÅ“ŪĆįüœ«ŅĶŗüśóŪRg”×”ų¬Æp0¾³™+ryyÓ”øźdv kø”+æŽļµśg˜ųl«†a¶Ó5Į`ūrLĶĘ}YėćqĢø=ųv×t3³ž›|ŽŪ„a{›ésK,CSJŲ0Ī†7ŪKuąŲ[äŪVŽŃ&c[vfĘBKēŗ”@Įn:ņ^°·|īMö_yóĒZŪĀ &³ŌÆ×@rĘB!΄ޅ Ü» …Į{·2nė*ę…u%dk9øĄe ǰ( d[÷qeōFʬ KÄ>õe†ėjˆ>*ł[Ÿ¶˜¼ŲL^04źo%S’;ķčoR)ŠR³H°ļēęܹl.ģL¾?U:Eœ›+ѓø5³®ėhšÖŖŗE Z–K!„BqāĪź_Ԋ‚aŌ¶j_·[ēąA.—vÜŪ…†šHN¶¢¶ņŲvémńćøŲĘĄøŲŗe3Ļ=ūŸ/ų’Ś:˜L¤'ÄŠoPg®7‘ń&a ‹FÅĢQŸ$¹v¹œ¬)‰ēš6v,{¼tkccĒśJ¼½Ćɉ†ķZŻĆ}¬Ķõ£”}Ļ£5EŪho°:ĻwŚBzT"Nö­ÅšóCQ[ŗdµ–ŻoŽ¶ÓŽ`eķ˜ó²“yh[ĆÓt?[ø”­Tµ–ĒłL¦².€a5i4ĄPī‚n6õČčP, ī`§v_9ß7M!)3–Ūś:čmʦių- T›Oń”ų˜¢l“Qż¬8hņv¾†ĖÉŅø;3ŒČ­5ŌÅ…ŅŁāåÓ¼¦fćßŖx“¢KZŠe£­E%ń²L>>jŅÉ¾0m{5³óüüŚ :ģØfžęjV”jGśļ8±=™1Ū34š?W}.Éą±.¦śq£Õņō?*š?÷ęśÆ…ø6’Ī„Šū’v<~t[^?Čwšü®Bqŗ’Ų5č\˜ŻćĘ«€)ągźŚł ]ŽŁāGP(²E±ĶÜ#Terāg ‹[ąæžėx”ö^ö©™ݹ¹!Ń«ƒb€į”qā7ø¬¢Äd0”ǒ|ž+œ†~œæ“„8s/ƒ“[œCÓ4ĀĀĀ iÕžž@€ŠņrĢ‹]!„Bˆ“tVĄ&S"ŗŽ|Ųė5X½ŗ†½{Ż“tm”(™B’žŲlĶDl6'7¹­®®®įēŚŚąö…‡ĻĀŖ©© *'%%•<TNHHhŌęąvŗŻĮ·Åv8AåŖŖŖ rćöEGĻŽ®®®*§„„5{ĮVXXŲģžgZ^^šŌ®ōōōf÷///’’ģyœÕ¹÷æUÕ{÷ģ;Ģō°8 «ĄøąC!\ø¹jŌäśĘ÷š{}Æ7ęFcLB®FnˆIĢ+q!y‘ˆBĄ("™apց™†”{öŽ»ŖŽ?jŗgz˜] ēūł4Ó§ŖĪ9Ļ9§ŗ©ź_=Ļ“PnnnN(&”ėźź˜Äö­Ös7—–!įjDĆ~ł‹'ųåņ_¢Dü¤+ł¹I“w„ČK1±÷ƃŹK=ĀJż*ŗŖņ׿f—;™ł%i|÷¶4n;Źv ÷1·Ć9GOĒŲ?ŪX£„Žć??ķ¬¬G©„iķgģżŽa yÅÜļ¶śƒ¶h@pŚ1GU Õ¢øL!ŅL> %?“•$GÉAsF ;u‰¤¤0Gy7xOxļē€TVćŚ/~ń§ŗDTUˆØfŠ;@ƒdI¼@Ō»UŠ%ĢI#¹eLŪ:vQÖzłgŪ#“ÓXXœŹčō®'kš‚¬ŲŁČóµ>Žż²›)¹ĪSźĶé oŪg»6ųøźÆ]÷{}µæ·}ęSšæ9_nką‘ļó^ŗ:©yNŠ_<Ąn'ĖÆ=õ¾ńĶż-ÜüĪqq²ž©ūŃaŠæÉÉÉ$''śx+ Č2MĶĶ"°@ @0LĪŖl6‰čsG‡Źo4ÓŽ>8?(]‡ƒ46FøöŚ4\®¾Ÿ~6™Übµē-F¤85båŠ_°łĻņoWŽ¢0ĆA†CĮ,+¤§„£§Œä@Ņ(¶ģų„Ÿ<ś=ł+,¶tt$ŗäÉ3s}ō@Gf$sõt…‹ż>^mā?ācSøV³į9čįvfķŽŚę˜žŹ„<RCd`aV×EĮa•łōÆ!QšnĘB˜AeW¬L©8äXo¶·„ØUS™”oFiH ­6©‰ūä†ĪŠĶ²™É#L„:•?ĖHjKˆ:5•1©ŠPݗǮƱŚž©mcė•n~65…‰Av«½ĻķĻŃįŒ]:c ų°yc×>c_}¬]¬ÜŽÜ›-@ œ^l”é­^Rd?9JV%ŒdŅĄ¤‚I“†lR±™#Ų,0k$+«›æŹ'Į `! ĄšŌł‚,ŽųG’g’ōf ‡Ä§cļceF„IÜV°Ŗö‰“k®akł%™ÜY’Å«{)y„&¾ż÷3³i uż'Ž—øšö̧ń÷{o»ˆŖć¾>EŲībpwAwøĢé å­aqržŲéłŪ‹ÕzÖÅ_×‹Ēće|ńøaÕÆŽ»wAž€æ?€» ÷ģkėźq8ģdfd$l÷ūŌÖÕ Ŗ’¾ŚŠ‡]œœ@ Ÿcä³Ł™Å2±Ļ}š¦³eĖąÅßī“·Gٲ„„_a«u’XmĮyžAÕų“j«~’ß¼j4ÅIPhÓ)°IŒvXČStœžf\ø8]ā’W/’ń ŃĻ‚ZKļ6š¹žŅ$:upDƒ‡:8’“ĘM#"”ķė’Īı’ŻÅwæ1–ß}ĮaüĘęļ`ć~•ń—ēń­ÉN&p2gz£śśęÓ"ōčŒ(NgĮÓĘ$sUž2°~¦Gx’PgQ’{ŗ‹’|Ó ,¤ō8Ģ‘n§$ßĪ”QÉ|ż†<¾šb]„@o¶:X÷q„1—ąß.MāŅ|;ÓĘ&sE¦„źąÕ=aF_–ĒæNu2%ßŗæ”ĒĶaÖWtō™ļv@$ ³Š¬“źą“nŠs¤)Ä1l\wy2Ós­ŒÉ²’c|³z؃WŖĀä_:‚’˜‘Ģ%łv..“3²§cg4ĄėŸ„W’ĆW²Bl=Fdū·®Ņ„ģ|%©ņ°ōńؚä_’Ē—'sY¾i£“Y8Į†SœĢŸäbrŽ•‹F8˜’.CP„]ļ{n‡|Žgģg`¬§|Gõ7öĻŚWϵ; k)ĮPI t0:ŌB¶Ą,© h/½ėeŅm&c{4b!äėgu½KމæŖŗLs(ķžéhZŌc Gŗ ¾½æ$Ģ\?b?ó²?ö˜īt;¹³$‹_nkąīņʄ}w—7²žD@,¼ą“ŠŃс¦ >L‹®ė§D_;¬^³–e/§¶®~Ču=^/Ė_ΦĶ[(ŪŗÕkÖöŪOŁÖķ§lÆ­«cŁćĖÕߊ_?ĶŹgW ڶuė7ĘĖĘėĉ)@š9ē¬zŪl%H’]²ļąĮ MMĆĻŅŁŌįĄEE§>Į(IV¬Ö’>ėīŁ³'ž~ĒŽ ūrsCGņÉĒ å’’Äv'OžœPžųćÄć§L™’P^æ~}B9‰$”GŒ™PĪČHO(æöŚk å””Ō„ņŲ±czĢSSā€Ē“P3&ńųh4qMĢ=ršō Iķp$†ōŹĪĪJ(÷ yŻ3ÄuϐÖo½õVłI“ē’Kēæ²²2”ģó%Ź'OžL(™8æg;öPŠŌ/<ū,Sņ’HŅø’l$9ķŲL&يY7cŅT$)ŠļX &¢,żŽ·łÖ7’•y_^DFŽCJ>ƒr‰ęļŸų§…²ż!Ćkµµƒ÷Ndr‘ÜĘ[żŒ+%ŒOåżwŽņT$‹E3ņøŽ¦s¬!‚ŖƒÖ«0„R¶µ‘Éó2łś .LįlRÖ0ąĄ©łą8ĖmŁüÓey\eƒp0JcƒŸƒ~P©= eL:.R0é* <ūgū²]ćòz fńõ)9X ³w{=? fńõ‹³ųĮå ²Ŗāi PޤukCēŠG-ģžžĆŌMü}Š^śĄó”GxēżffĢIįīÉTnĪ=z{ /=Įųtąńz©Ø¬`Óę-ÜóŪ‡T?3#ƒXŠ» €M›·œq{ļæļŽA{šz<^֭ߥ¢…óÅÉ(@p!]³ų_ōų„zēM„ń`±f\ˆė:š®”kFYÓTtMCÓ“Īæ*ķ-ŽłŪĖvŲŚś;üž·OپiS3ĒŽ…>Ó`FŒ°2o^Ś)ŪŽ/‘’ņĶ>ė•——Ēߟnø§ŁS^³fMByØšĪ;Źē»Üs>‡*÷OĻłw8}×Φ\Qń×_æ`5u4$:šŽsӗ2ŃäkÅ©¤Z-$›ŠŽEQ0)fd TY'˜WLĘĶßESą™ßż‘/ßx #Ż£®ų;’—ū>7_zrz:Ė—¤°ēåĆü¾AGŠÅč…,Ÿčē”U'łXż”Ų¹ēŸFóža~Z­¢_€s+ĪŃ —ß÷¹Ļ„’Ł$„f"Ė ’,#Ėrē_I’Œ÷’ ’d”%ŁČ.u¦,ˆ'¤Fä’č.œū¶Ļć×_ä¼ś“łŃkk(5`71™ĀČö(øĀąŠt¾ŗæ€CÆ½Ł‰Ē™J£+FgĒķY¼„]ĶŖö[ŃĢ€E³Š];ɓ“ā›c’„$»@)9+Aš‡¼źę ģ‹HüĒG7ńāń/ y\ļ~ŁH“Ō=4s_ĒõĢŻŪ3æ/ ŗ;}…€nžę„>ė4vDśĶ¼ōZžÆõ×ēiõĀ“3ŽG[Gˆ¢±…C®ēõz ‡Ćgųūķō1bĈ!ænżFŹŹ·±hį|VÆYĖĖ~¦Ķ[šx½q1ø¬|•U,Zø€ÕkÖv†RĪēž»īĄ]Ļ²Ē—³äÖÅTTVQ½w÷ßw/«’“–ŠŹŻ8fĻ,eŃĀł,{|9‡Ē‹Ēė„dś4–ܲ8īüüŹßRQYÅŗõØ­«§dśT–Üŗ8!Üóź5kq»óq°zĶZ;•UŒ/— ×ÖÕ³ņŁØ­«g|ń8–Üŗ˜žč'ĢžUJõ^ć>ŃĀłĢžYJm]=ėÖo ¢² wA>‹. dśŌ ž’ßżģ²"z\]’5qæ'ē8¦³Ż”Ółüžwé™]Óė|ę¶›šNmC’Lø\_+-81.‚üķ-8|„+.-Ą$é˜P$ ²¢ +†’Øls`RĢ„;Z±$§rՕ3ŁśŽ»,¾m’|ęr_79圜»Œl;¹’JKPC7›SčbT0@ev2×eг+ŽlāŠÉüĶ~FMH”ą,ÆSr’ 9łIĢT‚¬³øøvņ…0·ā=ݼžQ«˜@ œ6\~?×ļ®"Ó׎lŅ‘$ŻųńRÖ{¼8µ¬IH:dé-dŃÄ$Ó>t‡ŠE²¶õ«ų¢öųey€4ž;x%)siśG E9żTXļƒŠŹüģJ6žø„¶Ļ 8ĘŽŪ."Ūe<ōŪ]äķ+š™ą—ŪNńJŽ ĘŻ9€?œ ńw8”•o£dŚTJ¦Mc峫ØŲ½wA~Üs63#ƒMonĮķ6<|ż~?>°”M›·°ģń'łĶŠ'©Ž»æßo³zļ>**w³äÖÅx»ŠŁ3KYłģ <ųĄRŹŹ·³ņ¹x¢ų'"W°@ ĮyŹY€M¦\œĪšłžš°=žģį}B”SŪp:ē£(Łb„ē5F:P-ʝ‘L]SėėOP×f樾zqéŠ M~ŽŻó>ĪŖ:nśī0nÜ(jŽŌ ł±Ł]œ©ŠE9¶spę$²rķ§(ŲM’¦Ńī‹°ēh”¤,IāŌźš)› N“2cĪśZ*Œėd¬ ¾0Ÿ’še#õ‚˜[qŽžn„,‚ÓÉ“Cµ\zš Ā/ŗń’H|ÅEąĪ÷F(h½«Š®ĮÉHo¶]‰?bŻøŹGL6ŽožÅ/>śM}œ±Iu īe(Ymō€u$]gFJ S“ó^ĖŠžžŪļ 2»09a[Ģ#÷„«óČM2‹@pAP½w Ą¦Ķ[ČĢĢ lėv|`)™™†š;{–į!{Ļ]w°nż֭߈ßļĒļļ=_öųāq”LŸ–ļ7fzö¬Ņ„Ģ†{Eüæ?€Ē捇“ŽyėöELšW÷„„c332˜=ó Ź¶nKč/ęŻėvēS¶u›6o”¶®¾Sō6ņūżjėź_ī‚|¾}’÷ČĢČ`É­‹©Ž»ŸŚŗś^Ū…¾ē†h\[Wßēčö[’Ćž˜ŖŹļąpŲ„×’éÓNė˜ŻFś«XX阈ķvÄ·/Gff†8Q@ ĪSž!°$™IO’w<žļ£Ŗ†@yĶ5éŌŌ‰F‡ē l2ɌŻ%†*J:é钎»D½= IDAT$™Ä* Ī’ctŌ6¶h§Ł„O3³g_µ'™02›j›Ÿb”±°˜µo¾BÕń2rÓȻʊ• Ā„ņ'Ÿ™0Ё@ Įē…ģ¦VĘ=Ž×e³¤÷‚»ļŅ!9LČ„ńז+y©ķŽŅgÓlÉ»¢Q@M6^ŖŠ r2’ļȝųüN›üs&d‚č!P`X;…_-.KŗN‰ó.%@›:ų>ŸÆõqsm;?¼z$īd KwyÄ¢ .8üž•»Y“póęΉo’öżß£lėvęĶĆź5k)ólćž»n×?sī‚‚ø·lļmūq8Œ/GYł¶Oį²ņm”LŸJffėÖoč!öNeŻś 8vfĻ,eõŸÖāńxłŸi¬±Į½1¾ø(ī1oīŹŹ ÆąŁ³®'‰@ ĮyŹ?L•åTŅÓ¤©iŖŚDR’ĀÅ;OKŪ†ųū}d9E¬°ąó䚎7øĢķ" KšĘ=#²1K9œ<é!ŞIs&,ø•«¼ķ,h;L(ć"ĘO˜ˆl’qŗi÷Üu;+Ÿ]Åź5kq8ģ,¹uń°Ēč.(Ą]ĻŠ_?Żo;>š=–=ž$e[·†ó@ œüCŻcM&7™™?„¹łIĀį}§„M‹eiißC–SÅź >7Hŗ‚E’™ShĆIˆIN²Ž®©dä$ć …‘m“Ā—nYBūšŸŃ9Ivn’,Q[[KöČ1Ż[<G)““g'/ Ź«qV~mÜ~ŪHfÕē_ßó§ŚY^O„—g±0ŲĄ_„ų+ąĆUq# ×Īz쟘šŪķ½®®Q4Y×iOub–Uōp-֖®ƒŖƒI6<ف„Ī—lćå7°ųčF®šļA DOB$ ę‘ 9ć0ŗŽC 1I©į¹ųq iœKwyśõž½źÆµƒj'–?x0ō%Ō¦=óé€Ē?_ėćł>Žœœ+ā/Ąģ™„qa·;±œŗń÷ŻŹ‹ĪOȧ?7Wž ÓĖÖŲ’›OöŅkߞ¼±6ś²+ʃ,=„N̶žķ;v}ų”x¹»§sĻś½Ū+@ 8ł‡ĒG6<’‹ŽŽ?ć󽆮‡ÕŽ$™p:ēćr} I2©īĢ™3{}6ųö·’אŽ/**:«ö­\łLæūKKKĻh’÷Žū/C:Žķv'”o¹å–„rrrņłłI•-Œ™yļWm#5Ån1aŅĀ ‡ ź&L’ 3Q‚~?‘H9Įl6$ŃŚÖĀѣǘ4ķݤظižĘV¦Ź«>u oK˜ś ĮŁ^OÉé䬑:¼ī' fL Į9Ff«Ÿ¤Žŗ,”#”ėŻb=ĒÅ_ć ›h ڈx%e;åŃäf7³ÆuzPÅŅyµŃuTŻj·&Żżl’Aī€åNhčČakx*3:>DŖ×! ¢€yHį 5 §į[¦W9(åņ>I,žąœC­;B¤j‘½Ÿ z —ė+(J¶XQĮē–ŒqSIJJ!ī "ÉH²„Y3ƒ&s4 `ŅĶ”æų9YY\ŠŅę²9xŒ¼Ā²sG“ ’,ņ’ĘŃĆlÜXĒF1’ŅĒ$11āćēuB~@pŽ]&ź:ŁžvĢĶŅ)#”ĒD_xč`ŲDC›`Ō„ÅåõŽé¼Š6»K"d²b’ĆhRt#7pD×P5 zŌ &ĶšVdCü{ƒ1Qį-"t‘ū‘0X€@o5„csn§ :Ц2V?†UŠ€ˆ¬"8W>G~?”-Æ*ÆSšķĆūW×Ńż>¢{?!Zż ”ĶC²;°Ģ¼ Ū5×#gd łó+@ \č˜Ī%cd9•””»HN^B0XI8ü1‘ČaTõ$ŗī@’œ(Jfó(¬ÖIX­Ó‘$›XIAƜ·扷Æč’„¤Ų "ęö:"ķ4U¦#! éH”6R\éŒV:(˜ö%¢. OSˆö}՘]v22s•ŲĒż Šæ²™Ė.ĻäÖb£\ŠŚčć•wyķ„FŹŲ\žēzļ½ZĖļi [ųźnnōą;Æ·ÓŌiŪų+G³įJcÜ[’v€ŸīÓūmW—LĢŗ*—[FYČMR°”ć=ŁĮŗæ7²¾ACGfĢä,ī½ŌÅø$™h ĀŽ­GyāÓŗdå¶ŪÜ\}¤Žoo ¢H&¦]šÅSœŒv@óIƕŸä•ŗØį%<`½ )LžžÅķ;)J’ŃĆ*ǽŽx·õRßöuöwqI&w\ģbŒ‚!ÖæQĻ‹ĒõĻnė`ĘŅßÜ÷g_c?ėŁYļŠ";ĆĒŲßU@ Ī-M#µ%`8Ųź2š.£ėr—'°&!©ŗ*ŃŚn'¶d`BƱbŠź„Āf+fK$ 4 ISQÕ(Qłf2”+¦Nø[h€°F½'_Ŗ {SŚkē]|“T ˜R obUEŃĄ$« 7“Ķ#“ÓųNin¼\Ӥ䕚^™b9%owžÆ8IÕÉĻלNŁ·÷¶‹ČvõµėĶż-|ā ņČG͟iż–_’Iq¦RwRĀöW?öņ^½ÆW»^ŗ:kŗå9ŽVŪĪ oŌ÷;ö -ļšP„ÕąĘ?Üü:zĄ«Üķ·ė߄ØnļuæąęæÜü¶¹7`ūņMHĒi·S @ ų¼b:2¼zK±ŪKÅ HF 9³…+æż#Z>ŻJķŖĒ9q¢ƒäd&]!+M&ĶåDņ5sä£*ü!™ON2©p;+?įĖ‹ƒt¦=e&ĶÉŽ‹šņ»Ēųm»Āō˲łÖ‚,Nüį<É’Ż_ČsŅŁś'/“²¹-ÕĒSk§Iļŗć?TqŒ'?‰¢”ćo×n7¢ąi'Ķėį‰-!Āf3—_’ÉŻó³Ø’Ć *“ÓXzµļŽ<ErXpvDūpŒ(.ÉĆSįņžóBį„ īX8ė˵üń„ōßß®š©mŽ›‘Ļ£—H¼æ£‘7ØHIIÜ57‰ā$µžģ“7c$O‡ņ'ųĆ ³SĮ×ŖŸ[#e€¹÷g__ėŁ9+.Wę©ģ¬šr@ ēŚø ķfIz[§,u Į’fˆĄjDA ›H‘UR«)L‰«žwŚŪi(Wt†ēƤkȚ†¢E‰Ŗ%‚5£*&4Ł„Ž)ėĄ#ųŪe"&R/ątęn«ĶšŽjčŗ ]ž˜ēŒNKG§ŪX˜cgż‰”'ėø³ÄšŲ¼¹¶ūž~œš :ØzׄrM,,N呲†!÷=3ÅĀSsG2:½÷‡Äoœ”Į“2ø¹q·;„ī$īt;{‹/DĢf3’4šżd“¶ßsæC;Z‹Ąbī–<»Ż…ŚnĒč=öéļn&øs+I÷ż;¦ “ūķ_ˆæ@ IL@p> ¢£ ™¬ØšL•¤R4Dk‡Ł!ŲīĒoq`š4›}å›HĻI£`dķė1Y¬$ŠæCƒ%Ž$'Ł\,šl¢āķ:^ÜÆ¢ß1qÉĢŃȇU¶½×ČÖŪņų×9 £-ģŚRĖ{=~O ūĀöFāż Ųīć8s€]u†ļ‡f¦ßœĢeYU’B2•õŖOj@Øļ1X]Üx±…šGųUU Ųs,Œ#£K\üłoķÄĢķ«æŻ'$lJ×<‡pńÕiŽīŖå‰BDÉaf‘nüĄ&Ūś±ĻźāĘiŽģ<Ā’T„QO³­»ŽõææĀ;ĄÜļŪ>”Ž×3FöŲ$ŠC>^Ŗ?ŠŠåģ%‚säź[–ØĖK%¤˜Q£&TMAӕø0šŖQ»¦cS¢Xä(’Yåŗ“š8¬9üš"©HŗÖ)žŖČŖŠ¢E0k"rÕdB“ X“etEF— WuąŠŚQP Ń· ˆįŲzšlƒ†_wąft¬Ń6…)¹N;"qŻÆŒM–£ŌÄ+óĶ}z÷iOŗß\W€gŻaŹ[ƃĆs7øūō.īi×KWē č½ūŸ³ņx¾ö€ų@²,#Ėż?a*{’źēŠż>āGž"Čv}õ>6ė§nl Óń³’Āńõ{°]÷åÓ>>×KEe•U8vJ¦OeöLĆ1”zļ>Ŗ÷īgö¬+ČĢČ8§×Źļ°ióĘ1¾x܀ĒĒĘ6oī{ŸĒ­[æqŠmĘŚŻ“y ~‡Ćμ¹s]W @šŁ°@p~Ünæńč:®‚qȎ4,Ŗ—l§g–‚†Ā¦ź¤Čą‹W~‘‚¢‹ˆ†4Z£*Eœž7źCSV_Ø’XrJŖ•Qf™œ¹cųóÜnŪ;e$Ttæ/óń›kSČ©=Įļ(,Üī©hmaN “lƒčįV^®sqĻW )ŚŪŹĘ=­l;©Ņ›‚’fcŒ)ŹöśHWä<-‡Ǣ,ee¤ÜĪ>½æž$J¾8š‡'*†]j;?5ĀXS”÷kĆD{é3z¼oūLi6ĘvŚ£žv[{Ÿļīūš{s?öõ‹dff‘ö/Eŧ[ pZ1 @pN]~Ė4ä$’-ؚ‚Ŗ+ØńPŠ1XFV5œØ˜dɤEÅīńĻ#·‘ŽÄŗƒ³Łßī&Ŗ…pXĆfŸÄŻˆl…#ķłģ:1‘“Į 4ٌ†Œ®(†Ą,IhFŚį”CĘĆu  bˆĄ Fčg`RŠ5V‹ohx)pīŸ”’ھONĒiQ˜]˜ å֟’ŅĮøP{§ŪÉ]S3ā‚ņčtĖ/Édé.Ļ)õ~¹­!īyfŠ…ļNψ‡avZ–]•ĖU­ōŗ‹æŪjŪyl{c‚]÷_–÷¾¦(•;µõźįė «8- Ł.sŸ¶  •½ƒoåS]ś~{īHüķv¬’Ļ 9„ó_¾sŚl/+ßĘź5kńūdff™‘ƒ7½¹…ųÕ{÷³nżĘó°ĆagÓę·ØØÜĶ£?4ąńėÖo¤¶®ŽE ēpÜ-\0(·¢²Šæ~€ńÅćāóy’}÷R2}*Ɨ²­ŪĻ A] ą|EĄĮyį±+I`MĖ%oĪb>xa9­ ŒĖNĘfRšĶd^< ŁęĞ3 §ÓNŁ_Ž`ń?}ć3÷žd¦&ź’ Š£¼³ł(/H¼¹śÕĪŪw…1#­XĆ丘‘ÜŹk­}’&0øvOżÓuА%@ ń׿f—;™ł%i|÷¶4n;Źv Ÿ®Õ‰÷§SżĮ1žóÓNYZr”dtŌ¾ōó~ģÓĪ€G`ĀÜ “ ¹OÄiŪ rŠ‹Ł9*;vNŪē;YIā’L Ī5|N;ķV;Ŗß„Ŗ™PuŖ®"°&#EPŠ&YGRt0iń—MŽņÕ¢m”ääp ͬ—ŃĢȬ“ø’ü`?6Ž9|Ooæ™ŖćÅh’‚&+č’d\“¼LĻ;ˆ°!üĘ®­bļu@ ƒ źäL¼Ńį Ą_Õžy큶xžÜl—yČaŸÆ5rģVÜ4:.¶.žœ> ˆZަüćü>¬rć$Cš’ėtźĘuåļż°ĮwJˆēēk}ģk=ŹK‹Fżŗ2æ÷±­żØ)ĘzńätV|Ü<č0Ö" āoÆ!˜‡*žöu¬ń>ōī[(īŃŲ®_ų™mÆŽ»•Ļ®Āį°óąKćāfĢ‹vÓę·šĒņŸG”LŸFŁÖmqļ[0„ī•Ļ®bŽÜ9,¹uqĀ”LŸzZūß“yKēœ~wA>Ɨžč'Tģ®2`÷¼Ō@ ĪWÄÆĮyB,h³$AꤙüŽżž“ģ8ŽF¶Ć†Ķn¦Įӊ¼³‚ÜĀ‘8PudIžĢ}Ķ¶Rćé_V[BŌ©©ŒI…†źŽ½]“Fgr_±Ź‹Æ#|e·ĻIe÷ŗŽé€®ŠŽĘ2Ä=JlwP"©Ę±Śž©mcė•n~65…‰Av÷eÕę 5ŃT&囑Œ°ŹČf&0ņ„8Ŗ Ü_{s€›»™ēsLOeBž ©!҇&Ś»}U-!jUĆ„!1Äņé°u š{©ūśZO€%1ĘßĮŖ£"ü³@ć¢l«˜@ 8ĒšŪLœHN¢Ųk&Ŗ)†'pēĖ$GŃUI•@ŃAĘČæ+cääČH‰QÉ ŒŹj@²–Ī;pÕx¹Ln(zÖv;GŽfŃq ĖŠįż«EIRź)-¬FŠź†×owįWŽŻ tŗkPK»īņ5ąĀ{\Ø­i RŽęĶCm”ŗ Qų†1ÉĆŹƒū‡=^~xõHĄšę¬ūX…7.|1ß9`½™)–ļß?W·ōz\yk˜7ö·Ä۟žēģõøµŚųĀØ$F§ŪpZ~P’Į݃š„¾Qkć_żģŠ…ß»Å߁óū’°ÓØ1ęˆuė7ąŹ‡E ē÷¹¢²ŠŚŗzJ¦O×ńx½x<^ʏ£zļ>Ü8vjėź©Ø¬Š‡•Ž ž±:ī‚*vļĘćiŠ{ÄĘBR ubõŖ÷īĆćiJ°”;拋(Ūŗ-A܍µW±»*.Ēlķ.|Ēlqä÷* ÷g[üūÓļĒįpÄmĖĢȱчŠ­¶Īx@#ö·{’Õ{÷Q[W» ŸńÅćāó;Öį°ćńxćķw·y(!Ŗ@ >ļX 8׊uār/qéWSQ£Aü¾V~¼›1łé¼zämmåePw¤¹aׄā¢é£›$¢Į/G*²dźvæ¬#!uŽVėH$Ÿœ1ŚÅęOŚś7;ŠĮŗÓyō’<КŲ|,BÄlĘm ³łÓ «ƒžBķu¬k ”mń2ėÖ že’G>Š kzt§³ąD+G0‘šńŽńžŪčYl9ÅÉõłG<‚Š™)é2UŚ{ūM!ŌĮ«{Āüģ²<ž5āa‹WbŌ„ nγī|ĆYN÷gšŠåy|+ℬ rG'1J†=Ų›Ó_>‚ĆĖ–†(Q«[k;Ū=§ßÖ”®©æ_ūśXĻ…YEVZyųD8Q]ß³c\bą#d³pxD:W4Ռ—Ŗ™Š:½€åĪĄ˜µĪĖx5lCė°"éa$=Œ¬‡ō(č:’®ל:x;l?2šśö4zóŃ;"Ųš”K2ŗ$‰vpĶ„ļ3=’R‘č¬ÅntŠĆ“Ø.¶‡¦¢KCO)šÅü.“ņøq¹®ĪĒ;·Ķ,L޹Żīm¤[÷`jMP„¦)„Ćøó)}÷E][$ž¾Æ|Į™6…;Y~­€'ešū[ųBb÷Śß1Ś×s¾p{ҧųŪ«Gp×Ūö_"Œ“90™mL½x» 4hnoåO’–Œ<7—_9›äädģĪT“÷ į"0½ŠÅ$Žö籩±ē½z~ČbÉÄ,¾?C†p”šżŹ«CäLĖāŚxø"D ¹…•UÉü÷Œ .ßߥސJŁÖF&ĻĖäė7ø0…#|°=HŁńp?ķ,›Vf^’ĘŻ) fM„”ŃĒ3o6qØWYźmGy4šÅķ—äńc“œōńŅś“¼Ü0\oU•÷ß9ŹS‘,ĶČćz›Ī±†Ŗn¤mė×>4>,«ēŃ`_Ÿ’ĆC„j ĢÖ÷üģšDĻ€­CYÓ ~½ūz[Ļņ°‹YQ¶æ ">ķ“DI”SL„@ œkČ2Ż™ųd;NÕOD3ÕMD5“¦ k*rDF2K•¢ E Ŗ:ńū²‰š$,öŠ3Œl Ł"ųŲq2›×÷M䃺ń„4²ÉŽY `ź ’¬”1:³š›/{—†  Kōķ.K:h*ƒETD‡ē Ł=tņ{õ†WTł°ĮĒ”\'N‹ĀŅ¢d–ļoR»=C&J¶Ą SģwÕ™dņ˜®9 ŗ'éV™ēk}ܰæ%ž“x(łˆ/žxč]^Č÷ń§=]÷S§Šškģ„ųŪ‡w±ī÷xe5Žžę°ģ÷x¼C Aœ™‘Įżßkœ/gÓę-,Zø ¾ßp’}÷’™™Į¦7·Äę%·,¦¶®ŽÕkÖ²ņŁUø Ś}āēįńxYöųrʶn‹×Ł“y ėÖo lėvęĶĆŹē^ĄįptŠ¢¬žÓZʶncÓę-Ģ›;'ĮNwA~ÜŪ·b÷n-\Ąź5F%·.¦¢rw<ļńŸś @\ Ž“y «×¬eõŸÖrĻ7n /ÜE 0oījėźXńė§Y½f-%Ó¦%®1ćM›9p8ģĢ›ū%-œĻģYWąpŲY½f-Kn]ßc9ƒ{öæā×OóIJnjłõ(™>µÓ3ŪĮ²ĒŸąĮ–ā.(`Żś ”»· ˜ÓX ąóŽ€‚s ]×8øo7ļŁCķ‘ćlŪ±·Ž~–ö6@B×u¬&'[C˜’ēŅp:ģŒJOE29(ŹÉDŅ5t$.»ü2^\ż2‘}5Hšœh@Ń-dfgć¾č"Fŗ ÉĢŹĘˆ!×7v³ĢÜ )¼öaK’ĘkQv½œ]ļ÷rs½ć_Ż‘xþÆü‹Ź»¶D[Śxź„6žB»āÅÕūy1aS>ūŖ‰‡V5õ1ٽŌÕ£T¾œŹ÷|„žz©±éķz6½m”åōt–/I”Åį†~ģė{ÅūĒ©čĶžĻlė ĘŅļÜ÷o_oė9zFn_ækįŸ‚×LLĮj’ÄDĮ9Č”QÉL#õd;ÕLD5c–ĶØzE“‘£2DˆhHa“9Šbi„1(ózĶx"Q+Ɏ0Ŗ¢įQejvö¶äRÓ°(6L²Œ®EŠ%]–Ń—«;foęŠŃ‡»<ƒōČżŪłWÖ ©f^‹|ƞ1¬šĻ1/X_XMõ¼ė˜Ÿ)¹ĘJ׌ŗ<Ś–(®n¼÷¬«›0{“Żxlš„«óāblŒĘŽÅ/čµļĮˆĄ¾p’ĒüO„7Žē”\'K‹’ŁŃ  =ą7ūŽ ĶabmQ‹{ž}xļJ'ÖKÆĄžŸ/ījjš‚ƒĪŸūƒ’Ä£^8`¹ŪŽśāŽĄ=)o ÓŲ‰{3u|jÆšĢK˜c9ūcÅĒĶÜ0.•l—™l—™»'„Šš—:#ŽN»YbÅŌ4¾æ³Ē½b§š+9œ¤ž×O1Žęd;¬}[ā“c';—Či…KGé|”XgāˆłØöIŲŸB NlčŌO(f’ūĄ}ĆG,ßnYł¶SÆX 8±Z­¼śŹ+Ģøā źė5Iź|ģ_‡gW’Ėķ7’5~ ”µ4iIa»¶b›:æ¾ņo’};5‡kÉÉÉ&QYU…ŽŽ®A~~’÷wĻ`±Xm×āĖŅŁß¤ü@Ē)ū~õÖ ±p‚Y!¦@ ˆ3»(‰›.M!ē8‡Ęd²cb>yļ·`‰1Ė̲!˚Š$iH)¤w Ą:Č:²¤3#ó(²ųØ9‡¼¹Ō¶¤ć 8ˆh Š¢’–ģĒ=¢‘‹‹j™ZTO²3 1Ļ_ Cųiž1ąh·÷{“8™”Ž”{’Žévö™·7ęŒN”¼“(™¾8#Į‹wżŽ–ė-̱sļ“ JŻ]H¾¹æ%īu‡·ĆŲVV¾ ·;ŸŁ3K©Ž»æ~š’éÓā!©«÷ī‹‹ā±œĘ‡’iÓā}m:X·~CÜŲØ÷‡=.ݼĒy“7æEff:³g–RVn󙙝¢šĘ„±¹ ⹄cį³có±hį‘X Į€‚s”¼¼<6nŲĄ P__$Ånq% ņ«ēÖņ·Ā|ę–^B~z ¾“퓆#”’łØ«Ēē”E5d“‰źź‚Į0Hłł#yķµädē É& ų÷kóų?mu¹Æ`،˵ń½y¹ˆĢæ@pīµY)»l4ļ?ĪÄÖf%ŒYµb’¢FhIEŽź֑:Å_$Żøl— ÅeVn-3܇ *2>d"²„ÉÅé cw†1™ć aˆæ2ĘÅ,ļ/$ĄŗśĮ|Žd»³Ž†Č c’ćļkš‚”¼RsŹ1LN‹ ŸSr}†ŽxóŲ>ūŁVŪŽ§püŅÜxū=iģˆšŪ=žīžŗ`„­Žčī;ŽĪ«{r÷Ē#5³°85AŌ¾ŁéMĢ»¬w†}^15_½“ų`°ć¦Ū0O˜Ü%žö‡{śönÜ#”#sē¬Q„Ņcó<׋§ę6?–ģpŲyōį‡Xłģ*Ö­ß÷ŠóˆåÄ ±»e[·Q¶u[|ū=wŻĻ[;T[—Üŗ˜ÕkÖ²ģńåńķī‚ü>½”Ē£¢²Š’éÓ¶—L3B+w÷¶½’¾{Yöų“¬|v+Ÿ]ļ³»°ģ.Čgõšµ¬^³6a<=Ccßß½¬ųõÓ ĒĘģŁŚ+*«xš„}öĖłŪŪ|Äśé9½‰į@ BĪa¦L™ĀĪ;øńk7±mŪ6$©ūÓŅš{Ō±’pFų-#J“„,I ėȲĤ‰Ł“łMLŠĀ„„¼ō§5ää /LœÕ$ńóÆåó‹M ½z  f%±ōš¬&!’ ĮłĀ”±Y¼5£ˆ‘›Z1GƘå(ŠEV£Č’†,é(‘Īˆ=R—Ü•¾T¤GqYU\Ö0XU°Ø i†‡Æ(t‰æ=’‹ˆ‰æ*čØ'\čmV¶^D„}ü°Ę4³°Kżūįö^yį@÷\’÷š½RKwyŻĒ›ū[øłćC¶ķþŪ0¤œĆ5A•o¼VĖSsG(Ōn«mēīņĘ!Ł“bg#ĖÆu‹š¾ē@\ōķNe–×F¹øįpG|æćś…ųBšĀցÅßX“Æķ‘øl“Ĥ °“lDŽ6öQ«ė½><ģńdfdšąK©­«§zļ>üž™™é†Wjg(įŁ³®`|qQB~ŽīŪ /ÕÄż‡XJEeµuõq/ܞmv§{ųå>°”ĢĢĪ:3K㢮ߥ]Ÿś”ėĪ™„qßī,ZøĄ°„ŪvwA>O,ū »wćń4‘™™ŽéulO°Ćļ$x÷nŁhė±ųŲ{¶ščĆÅ=333ČĢČč·’Žęf|ńøSź 7d·@ Įē ! ē8¹¹¹¼żÖ~ś³Ÿ²üžŸĻ×M֐ˆżø$!I†ĆAģVX6+˜M&Ą¤(|oéR~šƒ’Ājµ~&›lf™ļĻĮ+4±ęż&M,”@  €Ż,sی n¼$Mxž ĮywēlęŻŅ1œhb~姘äЬ¢H*²Ś•ŖE ė†3n÷/zŠ$ć„J Ź•!"ƒE³& Lŗ!ž*$ŠĒ1¢@T‚ØŒ¤ėœaaMśuxåĢ!giQrB(äµzÆ\T)?ŅĪ5ysæ0* €;"¼¶Æ…·ė}¬?’]ĻWœ¤¶-<`øē¾(o SņJ LNćņ‘Ī„pŅ`xż¾Wļ“ēo‚mµ>nŲߟ‹ ™c¦S¶Å¤Ųg'¦pe½WDĒzéH'Æķ”š…N=6^īE~§ZbŅØór,­ś7H×QB5Ÿy\ī‚ü^sé‚!÷:»os8ģ}ę-™>µW‘¶·6ūĖåŪ½Ž`=\c!—‡²½?ob2čsž;ö¾ĘÖ_’}õŁ_@ ‚ ś6VL@pīcµZłŃ#?āŽ{ļå§?ż)’ļ¤££]–Ńu]’Ń%E7n—u]C’t$ --•Ńc.¢jłÆ5jŌi³I¾vi:_š˜Ģ‹;šxó“VĀQ],–@ ōĄb’øfb KfdźPĄĮyŠ/-‰ WO$£ŻĒģC‡PdÕ’,iČj×õ·ī®ŻJ†wo‚¬BnD†ˆjĄęNXŃ:s“čE• ¤±&Ń*Łł­óV6Zæ4¬±,ßß6h‘õęwŽC/ŽņÖ0iĻ|:¬¾‹_ŒåµŪ£»]9&RRRŲ±cEEE؁0FČā]ułgh%KKi[Ē`Įš]™Ūų©ć@ÓT‡ŅRßιŲeö¾K+żÓv§R\\LLL µk×Ö‘ß¼c.6ĀLqo¤ĄR9K\yl7Ėč©‹Ļ©ŁC+*Ų5]PT5ķół£¦²ØéZµR÷Ū¾$jŗnFōĒö­[¢¦cćć¢ėSŻ©‰Ńūߖ]~r|tŚÆ¢²Htż°}jRōņ­Ū ¢¦ćć}ŃĒ«8ŗ}ŅėŌĄ«± EDDDDä7Ä0 JSłø'–6Hcȏó°b aŪ1qŪeī »l»¼pŲĈ ž–€—CŲpČ ¹ŁYź§4Ė“:y³łé,Oo¢—c.55•ŌŌTĘÕæ’ ¶»¬\vŹ/Üi{€{j“j‚‘ˆˆˆˆČĒ4œg™ģŁ@ė°•¹Ćš•{}"™$91:Āæ2–eń·æ=ŀżéŲ±ćžł?’ü3'NāŚkÆĮ4ĶCŽĒ¤I“˜3gĮ`ˆ&Mr6l~y°|įĀ…|óĶ7“““Ͱaƈ‹‹ÓĮł-ņxYÜ<›•õ2˜ŗ,‡¾?’L· +iX¶æS†eŗšøĀø.ŪÄ“ģņ1€w}z’Śøm“@B2_78‰÷›œĀŠŌĘźõ+""""""嘀‹šažīVfD˜ģŽČ©‘ųWµėśüс»Ł?Ήš>©GĻØé…óęFMo‰ßƇķ¶QÓ3Ó£¦×n‰īA|r÷öŃå’8;j:95ŗ~[·D—ß”{tż¾›<-jŗYƒZŃõŁoū¬¬ŒØé€?ŗ¾ķŅ£{8ĻŻ>™YGżÜq‡/¾ų‚““4ź×ÆŲĖ/))”øø˜ .øĆ0ųß’>`Ź”)œrŹ)ģŲ±ƒqć>eųšßŃ A¾žśk¾ųā Ī>ūl]MDDDDD~£ Ó$”˜Ą“­˜Õ<‡F›7ÓyĶJŚoZCnŽ&źņItŹš9¼v·maŲ6v,·‹Ēdg¬Ÿ%)u™‘Ń„IYX˜–ƒåõ+ų+""""""ķ˜€-¦ø7‘ąļneF„)īō gaź–łWÅ4 Z·nĶG}Äȑ#÷ōĢŻ×öķŪłōÓOYæ~=))µ8퓁äääšžūļ“””Ä©§ž Ąüłó™1c#GŽÄ0ŹĻ“ÄÄDĪ<óĢ=eµh‘ĖŽåóM›6‘œœDÓ¦M1 ƒnŻŗ1vģXB”^ÆWGDDDDä7Ģ0]DāćX–“ͲF ųo HjQ…;©S”OFi‰”RÜNŪ„ ĒĖĪŲx6ŧ°9”›kSźĒq{Ԙ""""""ņ‹“š׎ł[°i;«g, `ć6’2ÓhÜ­ ‰uRøķ#Ąb×NZY©:Ņæ2ķ۷Dz,¾ųā †µ,‰šī»’!''›įƇ³|łržūß’rõÕWÓøqcęĢ™ƒć8†ĮźÕ«iŌØŃžąļžlŪfÕŖÕ“iÓ€””ņóóٰa™™™ƒA"‹ŅŅR€EDDDD(·‡p¼‡ĶńńlŖ“ Ž åæ÷¬h‚Qž»Ŗ{‘ƒuŌĄEFˆ„®ü­»uŁZ~>ĒŽ{ƒ¼cõFv®ŻLė!=IkrąōæK\łäŲÉL-'Ć04h/½ō‹-Š÷wŻŗue 0ĒCĒŽYøp!‹/¦eĖ–|õÕ×ĒźÕ«9ćŒ3ŖÜĻW_}…Ūķ¦S§NŌ«WŽ½{óĪ;ļ HLL¶mÜn·ŠˆˆˆˆČq*ĪmPqŽéż Ęī{RŻ›JE±n}@DDDDDŸ£µśŁ•‡Ćo¼ĆĮK¾ł!*ų»›cŪüüõ Rź×ĮS}j¬6 ];čIƼœ`ōMWķ蠲ߨ~¹Ų½~LōMŪŗ-Øéfu£ė»fͦØéZõGM[ś«-?1£¦Ób"QÓ[ƒvµŪ'eęFMĻ5]’™\ķć÷˜Ēīäõūż 2„qćĘŃ»wļ½”ø˜ųųųØ lrr2EEE$%%‘‘‘ĪņåĖIOOĒq²²*ĒxĀ„ ¬ZµšK.¹8Ŗ¬=zŠ£GfĻžĶ¤IßVš†ZDDDDDŽ­“]ĢŲQCČq«M²¾ """""‡ĻQ ߅±YkÕhŻ«6T&:±cõĘ•µĘ,"‚­£ż+Ō¤I5jÄ÷ßOŁ3/>>žāāb"‘½šäē瓐€a”¼hŃ"-ZDnn..WÅķłóē³pįB.ŗhqqq•ī;‰0sę,š7oVi"""""r|ø<ǧF£"""""ņ›qT{o2K°ØYŚ­`qé×)*©QYl6™%Ō·tÄ… ĄóĻæ€×[Ž»ŗ~żśų|>&L˜@÷īŻY¾|97nb芔“hт‰'²yó.ŗhD…ņ,ĖbāĉœvŚiųż~"‘†aģ ņ†B!¶lŁĀ¤I“ƒōķŪWADDDDä8Ö/ĆĆuĶ|<æ4 ĘćĪuĶ|ōĖšµżķĢ+P£‹ˆˆˆˆüŹÕš³“ĘėĘÄĒx„øƒŲw™ĄæR ōéӛٳ*?©ŻnĪ;ļ<>żō3ž~śjÕJįģ³Ļ"%%(ļ!ܤI ČČČØP^~~>…¼óĪ;{ęy½^n»ķ6 Ćąå—LJi޼guV•=„EäšųśėÆ?~<ŽćąõzIOO§]»vōģŁēČ}P¶dÉŽ|óMīæ’~‘_Q¹~Ś&»xueyye–£F‘cĘļ2h›āāņœ£üńŗ(..Ņ9ˆēŒˆˆˆČ‰čØ€ ŒP×­Õ8Ļ[eh?†ŌF™±ļ`„óm3&jŗuóFū-·«]īrźEMoĻ/ŽšNOMŠšŽ¶3śF+ŽżF² 0:HžŅ°}ōņżz='ś¢ėÓµwōöń¾jė—“ßöŽvŻ£¦b|Õ>ž£ĶårqĖ-·T˜ßµkWŗvķŗg:--Ė/æ¬ŹrĪ:ė¬*—„¦¦r÷Ż©rłļ®"GQ  ''‡K/½”P(Äŗuė?~< ,ąź«Æ&&&ęš¾Všå—_²pįB‚Į €ˆˆČÆH’:^ś×ńŖ!ä7+ĘėV#ˆˆˆˆˆüÕwžÅFøĘėzb¼äžŅ•ŸMĮ±£ƒ”¦ĖEī€.øcü~?III4lؐ§žzŠ)S¦Šæ,Ė⫯¾bÖ¬Y”••ŃŗukĪ:ė,bbb°m› &0mŚ4JKKiŲ°!×\s ¦iVŗÆŅŅRøīŗėxę™gŌų""""""""""rB9ŖąöA­ŸÖ¤>Ī;•U3P°qI™idwkMBFźA•®bß.OtĻ1’Ź©ø<:Y§śŌÕu3Ŗļ©–˜Rķņ„Ž?@żTæø„ƒ}ü""G_BB­ZµbńāÅōė׏‰'2gĪ.½ōR¼^/o¼ńß|ó C† aņäÉLŸ> .ø€ōōtŠ‹‹1 £źėtŻŗŌ­[—¢"„Ę‘ĻqŸū'±N*ķĪģ«#%""ŃÆ‰‰¬\¹˲ųńĒéŪ·/ 4 gĻžLŸ>ņĆ?0`Ąš6m @RR’ODDDDDDDDDD~µŽj؃I«Fė†Jä­ŪB°øĒq¢–†AL|,)õ3šĘśj¼o‘#Ķqœ=׬żÆ]ņĖŲ¶mŪX–ÕswēĪų|>"‘………|šĮ|ųį‡{Ś>99™P(DAA)))X–uPūµ, Ēqz;©ÜīcgFµ=°EDDDDDDDDDäŠÕpœć!hT’Az8dł·³ŁüóźO Ć Nn#šōķˆĒW}jåxĒ££}PBł[)ŽÉ Öó>G(޲p­:¤«fŽ”bä-„ąpłp’r!¶Ž£½gŒćD*mŪVų0 …B„ĆaĄžĄaaa!óęĶ£G„B!¼^/ƒ¦C‡ї†HÆ×˦M›Ø_æžAķ7‰D:‡a˜¦‰ĖåĀ4MLÓT XDDDDDDDDDä8Ŗąd'†FÕ¤ Kųéæ(+ØŁø‹Žć°iń*ņ7n§ĆŁżń%ĘU»ļ㕽įM.śŻŽž’ĖuUžXĆ!"n/ž£õYyx ÷ōæ ß ßrßIžźėR:Ž?öü7]'|Ą5 ŽrOėĄvÜĖžÄĒd<)nLŸ'bY[F°¬ ”ś7ņŁĻŻčŻŃ">VOx92v÷ ‡Ćl޼‰×S\\ŒmŪjœĆ .ĪGvvC¾ūnRŌü^½ŗS¦L¦K—Nå3yņÄ Ūwé҉@ ¤ŅeŅæß_“Tdš&ńń dffQ§N<.—KA`‘Ć쨀Óm?+Ķ‚J—9¶Ķ¼O&×8ų»Æ²‚"ęūŽĪžVåÉév5Ń?§ŸĘž…»ŸϼĶü¹ō¾öQž½ŗG#ll¦“挋Ā4®SyšŌŽō#Nž’į3^ćüäcūAł‘®KxśŻōÅŪ\K½™÷Š÷Į:¼ńįõdW7ŠVį[z ń¹ wJō‰‹< WŽÉ×Sæ%68³_DĻx9ģvƒĮ +V,cŪ¶­deÕ§qć\.—Hd˲(**dÕŖ’“Ó„˜˜EDDDDDDDDD³£Ī“ćpa`Q1-źęÅ«)Ž–÷‹Ė.Śŗ“Ķ‹VQ·Uv%Ҥn5ąšOrŻĆ‹8ł±7ų{ēTBę±Ä¬ĆQĖfۚóoi]õr'DČ:NRÉŃŗŲlųiNė!Ō3m6ĻÕfõ+‹”ټK’DB+/3ē[LœāĀ”^źgš•8Œ}/HN±iCĄÄ“y†ö-Ā„” åp>v„}‡Ćlٲ™­[·Ņ¢EKÜn·Gd?.—‹ääāćX¼xńńńŌ­›¹'5“‚Ą""""""""""‡‡ūčī̤Ą*³°Ā²-KVrł[–¬®4ÜĄNĄMU‘?‡āU+ŲšŃ—óĪčLа1-w/ĻęŁĖocģ¬ål)õŃ鎷9’©ó*ߎŪ6ė’u6ż'œĖ”7G¶ūóėŠtž8’vŽ\“–ķ„&µŪžŽ‹ś†™üßńĢŪā¢A’óō5tˆ7 ōCF¶Ż•B9} oŽv[ij6]o~ƒ·Ī‚ßšĒÜLžˆ‡Ī÷~ĮµóĪeteuųwƍø7¬a[©‡“–øņžø”g&Pśó»ÜwĒӌ›»O“!ÜüŌ£\Ś*p(œóoī¼ż>[\Hb³|…ƒ*k²ØŗLęćĖ€Čbž;·n*ĀŸŻ—ėż;7tMĀØvŸ{Į¶7/”ĒßOā–É IDAT°ĀŲę伎Ę6/f@ÜūLø«cŌÉjlO\½|,;Žļ/„uS}ŗŒ=ĒKļ™9/Ā[‡ō»lÜn„7…Å&)‰JÉ+‡—mŪƒA6mŚ@żśõq»Ż”••©aDŖą÷ūÉŹŖĻ¦MØU+·ŪiźŪ9""""""""""‡ĖQ’ĵ…U “н|Š”÷ļnÅŪó+y€-¬”j¶2HģҟN;ŽäĻ~…)kK£ū'‡×2{: ’÷TęĪžŠgĻoMÆ~'±ćūļYa3kśrūō$u߇ŁĢāł1œ’Ś4f’š!L’š1ŸĒqŻ+ß0mü#œ“äqzū‡#‹Ę?Åżs;ń·É X4ķ}9³qyą3¦OĢ]Ś5Ėłąšō¬ŖÖ&ĶńröŲÉ̜ö O)äå+’×׌P6GGŽĒ¢.óÅōÆx¬ėbüÓK,±€’ÉĒüÕļsŸcvŃ,[õ ·¶kĢU’]ĪŚU¹£c6Wżw “÷ žø ¦ąN‰Ćå‚O’•Ą‹ĒńųåAå«/ˆįµ'ćłņ•®ō%q?Š+ķ’”ZÆ}Ēž-..&11I"R‰‰‰”””‡±, ĒqŌ(""""""""""‡ÉQĒ;šZÉęG”C.;\¬0ƙ•LœS}2gWöüūĆŃō/|›ß÷ķD櫞Īčį½+q¤gÕ!5­>Y©^RœN·u_ņÕ* Bs™6'“޽ėUҘ~jÕM'­nĪ?»±ž:4iVŗĶqNßV/]µ]ü~¼kX²)ˆ/-›&™ž=‡ŹCLŒĖؾF,éõ³Ø›•K’’{Œ?“˜É{ćÖœõę¦Q}ižĶĄ/§ĆŠļ˜¶Õ&8aĖdFĪ4ŗ¶āĄ£>göZʙ£žå’ų‰‰‰dee‘@ii)7ndĒŽüū?į@-pÅź/GÄ/ ^…Ėš®~ļö xbKp%ł0c<`8%¬-A"6ĮŲND^@$ćd0ŌĆXōÜ‘ź¹ÕŽ}Ž›>‘L&»7RfDš'ÅÓš¤–‡„læć¦o¤>§f=>ĖfæĒėk2éŃ®!ÉĪVf½ž«ېRu°Åßćr.ō’Žæ¾’Ź„owĘ{˜ŚÅŚø€9ŗäÖM$»eCāĘSš”E=ßb&|¶ˆSzŁĢ UćÄŖė`oą‡Æ¦Ńļ“”N}‚~ŹeÄC ńežÉ`ó:}¬^މ #Ÿ Å)“mVĻIg3<ł"FßӋ“Q'S/²‰¼ŌŻÆ~FJ%uÉØśńxNŖzŸ{[× ķ¢×˜TzĆWÜÄŌGz²mģłœ±ä’˜öh*‹;õ†¶üĘÜǟž cŹ8É ŃŲ²ÉńQY).īøŠM—Ģ”`zĘĖ1gDŠš.æčsüMkći“ŽįŖśZÉß@`łķ”-oL Ķ_±“ZØEDDDDDDDDDD¤JĒ“;Y’Ć©‘ŅAŠŚŽS# Htj’µ)Ģ_ÉŌnęüSzŠ­’FĻkĘ­/ŽEæź:ŒŗsqI\ Ļā¼ŽžĆT{‡?ŽeŌŠĪ4ĖiĶ '·2ųžkčߛßߎUžN—žēpLJ+ĖĒ®ŖNˆåļŻĘ°=9ēńuō}ā®nꂸŽÜ=ö/4õWĪéёŽ}Īćö÷—–wŽ9‰Ū_yŒ^ėžĪˆ^éxņŻĢLkK“Zū"1UŌ„*Õķ3JsWŃ¢ckÜ™7{ĶŪ·Ŗņ NJ[JŽtI šĮšś©ł\Yw5'ūV0¢öj^ķ½ƒ’üĪo”dM«Łõz¶Ė1g”®'öūsII™FRŸ¼™ÉÕÜɱÄwnDJėbāg_‚{ćgjH©’qź¹×īŹæčģśē€ćą8ą8åcóį8ŲŽ³k¬>Ū¶pl»|ģ>Ūʶ-Šņ·3éó÷Q%l¹v²Ō•O„_6–¦‰A3+™ÖVźA§}>8aJ ƒŲÅsxīś[YzéG¼tV:G·oi5u(ż‘m’M× pMƒ_wŗXĆaĪø _Žt\…åŽ!hÄīöNbÓc^ߣ³g1xšP]u~E,Ė"——ĒüłsčŲ±3eeeÆQĮmų§^Hr;īŚåć•Gv”`ˆiJM."v0Bį“Ք4’HF9!łżž=×Ä6mŚ“’’‚ĻēĆåŅ8ķæ6-:ö"!¹6¦éĀ0MLÓÜõŪ…aåfł0†a˜åÉ: c×u±üāh(‹‡Č>ióŻ}›ˆˆˆˆĄÉƒĻŃżžˆČqĪ<>*aŠŚJeHø!Mģ$ÜQ-7&ŁvƒĆ ikÕ>ĀĮ_ ²˜ē†·£eß[˜Õõ~“ƒæĒIŽŽéÅźž,„ķ”4®%”ˆMø,ŸP°”€;ā—ź’ńqü•ß6DZńžt‰¹īŚń8›¢i+(˜ü3Įµ;”†ć š1n»6ÄæšNŒ²ĶĒ÷ƒ¶7óÅCW1źµeÕg ‘ĆŹ}>+eRßN ¾ #$5g˜å?"ĒŪ5mÅ+ų[e”OŲį­…$tĶĮ“žĖ®qŽz)ų–~A8x3vLķ£ōüJ #;›ģ:‰čė"""""""""""Ē/EĖDDŽ #”GLh.®ä]ćT›)CŚąIOąĖÉažx)?ĢTŗķ·g_rŃØb>ž:“O”“¹iĀŃ{ ®¦\ņĻ÷xqdK<:¬"""""""""""Ē-€EDŽäE6žoōLĆ æŠį–Ń„|žm˜??V†µß@¹_}fČE¼ōNÖĶ]tm°Į“–€;ļǃŖ‹“÷%·õéĄ)B A¾»³Ķ=Å¢ŻūMęĻ'µ`Äė›)Oŗļ÷ŽHZ¶¹qKxśō xl^ł˜¾öf>’ė% =¹+ms›Ņ4·=½Īś’ž©ŹF4vŠfńųmč2ņ-V†÷Ƌˆˆˆˆˆˆˆˆˆˆˆ. ‹ˆɋlÉZ\ GĢ Bįņæ ‹l{ļ²y‹-F=PŹĪ‡°å°fƒMdæ±+>£dķAÖĘ!‰± †vŻ;³f.óņŹC¶ÖŠŁĢɳų§1’Ēł8ķzŠ9v’¢ XöĆL¶7æŠææüÆüćĻ t}ĖĆ׏fņ~]'“‚·Gż×Ķ+ųēß/$Ū³]DDDDDDDDDDDäpq« DDŽ +€SqŌ܌Ś&ē ņņéÄ0WžćųO^åä$ƒQWųČL7ŁŗÓ泉ažśt/>·ĻÕŪİKŖ*FŹ ž6uŠži§KOŚń$3f—qĮ@[gĪd}\"Ęģ,ŒœBgc ÓgŃō¬n¤™•„©6ˆĖéJߞmqѝnėłžģ™ø BßĪ»V‰lä³;åįµóÖ(:'•ÖEDDDDDDDDDDD€EDŽƀoöó×Q~ÜūŇdš\}AĢžéĖΊ”¤l枲‡”欑֛~­äå)s Ś‚S—ŠžŖkń’ėS¦Æ“豝Īō 9łäFøX~Ąņ̬Fd™łäåļīĪl³éŻŪ¹µ,“kž{Ņ”tBDDDDDDDDDDäHÓ§ń""GRL-ģ@l§Āį8øM§Ņå»LüDĶsaoź!¾Ō£oæfģœ<‰…3łvv#ś 9ƒ“[Æfņ÷Ų4ł[–döc@3WŠ3Ün\ŲX{ŅY¤tĀÉuWóźŻO3³PéžEDDDDDDDDDDŽ4€EDŽ ;” ‘¼Rœ°Sįē£ń!ƾdīĀy;mʊ%Ņ6k×Y|1)Ģc/(*°+lŁQŠßäkē¢ńiƒh¶ék>ū9ÓS{Ņ«a½OnĪāńļņĪ—s©;p­~q®_ó óĘ#ōŻž"×ßō«#:'DDDDDDDDDDDŽ$„€9‚¬„f„м8„0Øe³ęFxw|yDŌeB¬ßĄ0 r„Ź×‰õĮeƒ]Äķ·mhs‘=Ŗ.NŽ—Ü~ꟙ=ą|toOāW£Į k5†ĒŸßLćkŽ„¹ĖÄ8õ4š?ł8/Ś¹öĪևüBį©&>»š=ÄMĻ·įķ?“&¦’ŗˆˆˆˆˆˆˆˆˆˆˆČ”S`‘#ÉC8m ĮõyŲÅVŌĻ­ē˜ ėnb`ŁPTāPX¼7ų›™jšĻQRŻvŌvVAˆ@+­ĒAWĒqœčįƒ] vNW_-*R…ٳg1xšP5ÄÆˆeYņņņ˜?;v ¬¬,śB[¼–„iæ#.+c枼°hĆ—?9,Żä CF2tkfrjGƒXoÅż¶n£8ė’5¾TAN8~æĻ5±M›ö¤¤¤ąółp¹\jœ_™{‘\Óta˜&¦iīśķĀ0Œņæ  £|Ś01 ʧ1Ą€]’a†T~óĒA÷m""""r<8yš9ŗß9Ī)“ˆČęÄ7 ¬į5øÖ¼@LBZ…å-”EßżēZPvIōÜHؔ€Ń˜p£jX©@`‘£ ÜōJ‹WĄ¶ÆńĘÕžEeX”ŹH ¬Ē?p ]¾EDDDDDDDDDD¤"EDDŽĆ$ŌįXų(öŚ·ńĘÕĀtyk“©ćŲ„KwˆmF óÓ8¾tµ§ČfŁ„!›’ MŲrˆŲ–żėµŚ”<é–楖Ā0– ķźūIŽÕ[D‘•>Ż9JĆE°õD2NĘ»š1bŠWįöĘazü†¹ßʶ"*&äų7¹‘pö%źł+r„…-‡ÅŠƒ6åįŃ_·Ż#-ŌIņ±ޜ¾“¼Ņ—öØMƒZ^"""""""""'EDDŽ2+­e}’KhēOø7~kēL\e1¬RĄĮ1c°½)XI°źžB¤ĪW¬NärŲQ” ŌĀł ~«|chœÖ:‘µ;CÜłßõtĶŽćŗ~éxLC'‰ˆˆˆˆˆˆˆˆČ B`‘cĮpa„vĘJķ\>iĮ Žį—öļ,"G„e;l.S¶Õ»4Øå嚾iücĀVn rϰLRāō¶QDDDDDDDDäD č‚ˆČqĄ1cp<‰8ž$pĒ*ų+r”X¶Ćś<+“™ģįŖŽi,ßąO’YĒĪ’ˆEDä·š¾4TĄĘU«ŁT[ˆˆˆˆˆˆœØŌ•CDDD~“6„ [ žV%;-†>Ķ˜“¤ˆĒmäѳėćq+“ˆcöŽuÆ­„ę—?Ƙsźa:„,|ó^īzw)e1ٜsļ\ÕŖqŽĖkswP²Įōą‹O!³Qs:÷Ģš-©åŽæL«āž<øåµ»˜PłõĻ)YĆä>ąÓ)óX¾)Ÿ2ü¤f5”}3øäÜNdøN ×Ęā‰Üw٦YMøņqA–¾”("""""r"RXDä0óūżj‘ĄŽ’ˆzžÖĄém“™½¶”%›¼8y7ōOW£ˆČq&ĚOēÆļ.„ĢU‡£ībd›x {ŪwP“1½~⼁¢­¬˜»…ó¾ēó p’½ēŃ2vߥ®ĖėĆēŁgžĒGUß}qņ~ą™»žąÓµĮ}F/fėŹ¹Lp·å‚ó:XMéŲX¶£SJDDDDDä§°ˆˆˆüę„-‡‚RK Q^·AĻ&ń|>æ€/pf‡d²R¼j9NXlžō ł×O)œtõ]ŒźU{汎LšŽx‚1ēԃĄV|żO½ü=½Ė“ļ¶ćł+rńģ³n“O–÷*>Š®¾{éY>]„Ųl†ŒÉšĪ Iu•±uõB–Ó–z» ±ó˜ūŃė¼öŁ,ŻƟќ^g^Ģƒš‘`ÖjʍĖēK6°-æˆā A|fō~9× Ģ!¶¦ėX;łéƒWyõó™,ßi_·½ĻŗŒ+4Ęæ{§˜%ćßāµ§²`C!v\-†ż÷Ÿ¾kydcÆĪXĄŻśj^=”t%€9a(Ÿ“ˆˆˆüęģ(Žą N5Õ#'—i`Ł/·M ""ĒĀŁ/ó—§æc‹•@»Ėīę®ÓPŻWTL_:m‡ŽČMƒ30‹ “æcń/āÜɛĪgÓ p /m.ž3<­5 SˆON'»}?¶OÅpJ™;önīū ó7—bz Š7ĢeÜ?ļę®’¬$ `ocьy,ŪøƒRbIōŪl˜ĒgĻŽę_så;¬É:”1ļ•{łĖ«“Xœē¢Vm?eėēšŃ˜æņ·ļņv½ņųłĶ{¹õŸ2sm¶/ž˜ąvŠų½pĆKRF=²²ź‘•‡K§šˆˆˆˆˆČ E`‘cɱ0ŠV`¬’cł›Ė_ĒXó!ʎŸ0¬€ŚGä°l‡āąz’:E¬Ÿł9S—¾0ń‘(ó(Iō»h\»<¤ņćšR ź=-"ĒŝMóę°>dP{ĄÜsV5ˆĆKóÖM‰1ĄĪßĦR'ŖĢ%Æ\Ļ ”g2pč™ zW¾ŗŒŹ®zÖʵlˆ8ąŖOĒöiTÕAÖŁ1‰·>[OŲØĶÉ·¼Ąß}ÆķH–|ų?¦—ģ³²‘Į°ū^ę7žå†¶> {;Ó§/%*F]Ķ:Īöoyė³uD<¹\ń÷—y턱üė†ŽÄ’ĻŌ/¦±Ćgēd^ūp’čzŻÓ¼÷ök¼’īk<ń»ś{? p5äģæžƒ±Ļ’“oīGŖz’ŠˆˆˆˆˆœP”ZDäX(X‚±ģU\Ēc†ó0Ż^ Ó`[ŲvĖ6°ÓŗC“‹±ėöCߣ9JC5÷7ņ#/]u!‹Æ›M·ę‰–ϽD™GQ“tĖ·±l‡ļ—1¤m²N&9Ę <^7‘p˜“ĒņĻöõłÓÉujv“ė8ūU¦;.‰dßī>Æ&µb«(ŃØŁ•<²r)ĖĀFRGN뙆ȼׇÖcbZÉ ~^oŃ;{æ\©ä6«9w=…y…X•ݼW²Ž³r KCŽó3cÆ?—±ū¬nnŪĢV ’–/ę砃‘Ō…3ÖĒg.?±.pB:«DDDDDD~ 9šB…˜sīǽī#<¾x\¾Xš×­ru»d!į×IhŻy4NrKµ”Č!* Śj„_ qķ˜=Ļ\]¢°ˆL²Ļś׎åŸÓ6ņ͘ˆ‰{ˆOJ>Ą—lJ™ūćb‚˜©õČņQeęœūpĘveŌ„Ži°ÅZϜyŪ¹°AśażrŪ³+!³mc×pÓ)’mxÓēĢĪ{Ē Œ„–¤¹gwyęž6Œ]ĮŽŠ‘‹ˆˆˆˆˆČ‰vĒ,""GGńZ\_Ć·åK|‰ø¼q8U|Lčģś1Ż1ÄħćoĄ=įlĢuŸØEQŲŖé'Śa~ŗÆĶ3ü4ĶHį•īš½–I_ĹݲhŪ°.żĪø–·ę”§uvŠXüĘõ\Ų„.­ź%Ѿm'žōīš}>øßæĢLśc}ZĶŅ=łE-–ž­­:ŽĢį}Ŗcodü½gpV÷Ft؟@‹F 8ķģx}ęŽčĄ€µ‰©c.åģŽ“Ŗ_‡“]Ę ßoŽN_Z“uö“·wȵ;ÕELD޾†œ~Ė=\×1 ĀėłģÉ'ų`mUƒś:„ņW3õ­Ēxź›Ų†‡ĘżūŃü~-ŚHķDĻ\/†dīėńģ׋ؐ_BiI>—żČW3ÖÜŁĶiź1p góå”mD³~ādę‡ŒŲlše¾vŻ ÓČeąD §õ漋/ęņK.ę¢3rśiH3ĄUæQł:…?ņÅäĶ”_Ń#””ĮKœĒk;«×––æ'X(,"""""rbQ`‘£ĄlŜt!>3€Ė—Ą†<’™‘ŹóČIF­’ĶĀD6å{Ž9ųÓķĆļ!šĆŸp\>œĢSÕØ"æPÄ®éĒŲZ\’.Ÿ…‰IB=?P¬‡Ē6ąźū’ý™;™ņ÷›xšŠ;Ø7łŸōZ’ ·ßń9·¼Ģ›ż3±·-„Ø^}¾q·™)ÄŁ½ˆłą[fn¹f™&8;˜óĆR|]ī µgŸź8;Yśżd67»‡'é@LŁj¦æ:šGΟGŃG_s}› Œ¹žŽk_6zēĖܚė°ō?𷋇ųp"lļ«į:ÅÅģ Pä—j `9ŽxpĘ-7±īևųdżĘ>ö¹O\JKļīl–¾1Š3ß²…­ņž­†›Ś']Į­ēdļwSl³ü­›9ū=#źõ óõĻqWßøżŽąÕašµ1õ®3·p’ż>ŽwqŹŪŻH×Ō¾\0č沁IO\ĖŒē¼„KʈC“aæ£[>Lļ9ėōć‚~Ÿq’×[˜śÜ9÷ÕDāŒE%1œrļXnīģĮ¬Ūszć”IŪłö©ß3ż„Düv)VæēĶæ“£}K?g0éńk˜÷//A’É<ōō哊§""""""' ŻĀ‰ˆaŽccĪø…£—'ǁ1ć3ŲRą„aZ€¬Z{ҵĪ*eƊ žøŻ›1¬C>†įĀŸJ錛±5®½™ļĒž›yŁ#¹®ŗŅZHĶN›ƒČķKoJ³Üģ=ē–“’ ’~}#½łŠ? KÅZ>ŗ–É]ęóOŃÓæ<'…ž=śŅ¾MŠžĄeöĀIž›˜8i;#F¤cfńć\“ö醿ĀÖńĶśrrßNø€^żŚb ģĒ+Ļē²g‡›’)’ze ĶGĶąĮ‘ĶqŻŗ6”äēī¼üÜē\łĀpāk°NB%mėŻū „•F[DŽ/FB®¾łlßž.ĖÖ|ÄSovęé+RHIO#ić6 aB.±Édå“¢[’Ó9£wc*$cq°Be”D½=óˆ88P!wKLć3yš©ś|ōŽ'Lœ½Œu;K°ĢX’ė6¢UĒVŌ²0bé0ņH~…×¾ü‘å;ĀÄÖiE÷”3ņŒ¦ÄֆH¢Ū ń@曼õĶl–m.¢ČŒ£vć&¤{Ā8x0ŒdzżßCÜS÷uŽ8Ū )‰©EĆD„F*§\?Šõ’|ńó7’ŸµÓ¼č²/"""""rbQXDäsmłĻĪéøćÓ0 8«s– I!b*¹×IŠ0ź“¬ŁCzāŽ4††é&ĘkR6’ œ.OœŲ žÅ聗ńiߗųś¾ų~IÖ:¾zń~øō<®ķŸ®“MjÄ4Įś…d[+沤“ˆõ£rh}Óī¹6VŲÄ·µ×9#¹ŗĻGā ×p§ŃéÜQtŖ®BŽ:tq+ŻGT²,½+WŻ×•«t¦‰ˆˆˆˆˆœø·Īj‘#ĖųłE<»Ņ>ļÖ 5Ä;³Āō~Ģ`ŹņŠ!žā \ųÜņ~Ÿ'z;·7÷ŗ °ķJ nNMź&įŅi"G‘Ū4jųä­džć€Y‡3ĘLį“ 3vżĢä³ļfņŠŠ ok.}sŸæ2ŠÜMÆšēÓŚqń˜ŁŖ+ÓH„ßY§ā™ņ>¶…YõŻD6ēžFļ̚½M3L³¼^å¬Į8Ī/˱$ø7ķsr¬žµ"""""""""Ē+€EDޤąNŒ³0ŻŃÉż‚ųx.„-ųpNÅĶ~Xe°z¬Ļƒ©+*FŒÜn/ĘĘÆOģ¶q5ēņ}Ģ+×¶Ā£3EŽ"«&`?~åFK]ŁmhāŻĘā•õ›6'{ĻOSź$ī Šń4ģ{-w½=±×¤3wģ+ĢW]&$ ¼’ĮÉßńæ÷'1ń«„42”F5‰±Z+™=s3¾Ü64pƒ+§#¹žõ̜ŗŠ=įŚČrfĪ؈æUG×pŹä•ģ 7ØåՉ$""""""""rœRXDä2ņęćvU¼ŌZvy 4T1µļ¼ŅP%oÆcŪ̃Ŗ‹³ósFuÉ„×_&SLI·¶§~æĒX°»ƒqh7·iÄŁÆl¦<Ń«ĆĪw.”qÓ«łØ°63yĢļÜ© 4§óąėyęū-{HöV&<6’a½ŚÓ“Aź7ķĢ5’ٌ]Õ|k1OžŅˆžĶaOēšz¾~āNļŽŠģF¹t9ó&^™»O°,ø’’z}ZgSæQ+zž’8ß8Už[¦~ł:ćfåA­a\>"›•Ļ^Ÿžz‹IS¾cźWoóŚfP쀵ś3Ž|k3ēĢeĮ“˜±4’SI6Ŗ.Óš÷ę‚ ²™÷üõ¼ŗ —Ó†4«¢g¼Åę/žā¹·>ęūo?ęµ›/ć¹…8ėŠÓHŒäÓ¹źŠę,s)wż”SĒńŚ-—ńü’\.łżąÆS™•Ūƒ{ž>©qœN$‘ć”Ę9’Š×`ø*ö”ó{”U¦ĮÜuVLĘŚ6ĖĮėہ *kŗÜP¼ņ +ccE,,ŪbčŠ³3¾÷~bNžCė4ƒČ²™ĢĪ ³éǹ/ƃŸsgĪÅi3]āĢ~ōb.}†ßł iīšógō%—ųąnmĪ~’kßĀ3t%9’™“†é,­|>yūÕÆ”£/āź²øį3ŗnߎ¹“»/染Ÿ |!“ī½ˆž—Ąy·?˃ĶÜlžįĘĢvšVśE*ė5)ĻÅ\ĶybŌbŠ_žĪ7ŽĆ˜+?&’CŸŪ;3øs ŗŻ;ŽēRļā™·oį’ž(‚„,š{ētųmćŸ}†GWļ$äN&«ŻéÜ9f-ÜU•™‚ ¹—Ü@o`ZĒQ Ė©ŖūÆAŒw'Sž¾ŽēÖIĢéˈž`TųŻŽö·Ąó±·ó·\ɕ۠v«ÓøīõĒøŗƒ’ Ö©hÅÖņDÖ.Ó WӝH"""""""""Ē)€EDޤH¦×…įīqhO\?¬4Ü wt/ą†õął+ˇõlTo÷ūlo»”,pPU1jĪ3³Oß3ķtėCF3mV)ö³uĘ ÖÅ%b̜Ęüšit13uF!ĶĻėIZїܿŹ2ZŽś†'®l‚ čŁ5›āŸOåŸĻ}ŵ/ %“„Ü>œŅ«żŽ«ŗł{9ł_šĀė›9yōÜ<“Šś‘õLčö7>žįaśuų’±ļm¢ćmļšųȆå),ŗ%³šŻoų”ŠĒ(R—icR“Ŗ]Ļ“}Œ»€*,Č¢ĻMÆŅē¦J6:é6^rŪĮ— ˜µš“^ ļÅēRõšæ&)żą»:U=v¶+“ž£^§ēØź”ė죰ĢbÕöņtőčÓĄ"""""""""Ē+€EDŽ0ĆkbĘV –dĘĀļ2«Ž®kėŖ—9¶e‡XÆō¾ h{/LžChP+¦}’3~’bŸūˆ)+,:y§2u}cōo„³üm•ÖcP÷†{ƒNīĘtļV—'¾žĖŹČPڇVŸČŠ,.-fŻMhų§=+ląßRDxå"–…ź1ų¤zæ@Yj¼›’ Ć±ī-^ʆ‹)rŠ™7öž—p=Ɯ‘Žqœµ×”åÅX¶ƒĖ4ø²Wm@"""""""""Ē1€EDŽ '¦ŽaWč|Čå- _­C+ÄĢ¢’)¹<üĪ7Ģ+,bā¬Ęō»k8qSĒšĮäuœć™Ąāzx°¹ę…0™ć€™ĪY›Ūķūņd—žŒ±lWŹ^„w–ĆĄć2HŠu‘_9¶‰,㣛óĢ"7õ»_Ęč—o„•ēųj«PÄaźŠņQµ·N"+Å«HDDDDDDDDä8¦°ˆČd$7ĒZØ6l;°v‹Ćҵ6”d„ä64ńUc±ĖB8‰Ķ±v.²‡ !÷Éwłß‹[™R»7×5¬CB’\üümŽpĶ!sš]“ö€»I[Zł_fś“µXrŹ{GV1}Ę&b[µ%ŪM…”Īż‚”Ż’fŽ—YøŹ¢įٹģsš“„•’_|÷ķ2Ā[āŃé%‡(5ĪM0lS¶į;±v\’åV®ÆŃS¶5ų&?å*Ž›—Oa™En]WõNӉ#""""""""rœSXDär›c…ćĄk`˜“ŗžō³Ķ£Æ…˜»ĢĘŁ§ckZŠĮUgŗ¹xˆW%±ćHQ14ļspuŁł97 ŗ‰Y_ä‹ūøåĢ6O2ś™MäÜš¹nó“!ä>ņ0’“sųýmŹ­I§ńū+šrö˜k¹5ī6Īiī°ų?ńō’f\3śŌ]ć’£Ö ®ń4>{-æwāĀ.õš–ndI^CĪ?·3 IƒøžŹfœżĢU\ĖĶ\ҽŽ’™¬(qŖ}Œ"UžsŌIņ°>/LŲ²Õ •X¹-Čä„E¤'zøėōL|¤ā(bŪ6”PˆņņręϟĆ1ĒōÜł‚U«š}{ ‰}óĮښ=7į»(|”Wg‹ęy&©I&†5u.k×;Ģ[lcXpēļ·°Ö~æžŗ”?ą“ž:AŽčĻÄĪ»‘œœL Ą²¬Ÿµ>.PZ„¢ÖĘÕ#¬. óĀ7Š:žĖOHĒkī{ęoūcśŸ”†iZ¦‰išß- Ćhų·a‚a4ül4|b`@ć’0 e‹ø[† Ńu›ˆˆˆˆü¼?[×{""‡9„rˆˆlń͈ä_EķĀē‰ķŽtĖĖ'Ÿąćäv>ŃļqĒĄØ`Æ@؈ˆˆˆˆˆˆˆˆa9TLN÷; "<ū>1«šå&ćMĆšmdrm»2DxŻ&BÅvĖ«q{\ŖĢ_Ł-€EDµ&Ēćdö£¶d&”µć0~˜†å`šu`€ėx±IĀI芛=·ĖIąU»‰ˆˆˆˆˆˆˆˆˆˆČ),"ņs0,Hļ“ŽŪĘ0\\<ą‰ĆT;‰ˆˆˆˆˆˆˆˆˆˆČ>QXDäp`Ą ąŖ%DDDDDDDDDDDä'Pz™ˆˆˆˆˆˆˆˆˆˆˆˆˆČQB`‘£„Ą""""""""""""""G €EDDDDDDDDDDDDDŽ5ˆČĻȵ”j5FÕ*×_nlSŒ¤öøV@m$"""""""""""{M`‘ŸCÅĢ„’Į\’ fd¦ĒaXø®ƒ`»NzÜVāf Cƒ6ˆˆˆˆˆˆˆˆˆˆˆČī)š "r¹įJĢi7ąūl$1Åć ²šSšĘ$ā $āIĘŸA0!`Ķ÷ų¦\õł™Pž½PDDDäØ91,cĪ‡ÆšöŒrÜ£±¼}®_”oæŁĄ£ "8Ś;ä īk6³g—óęJūš<DDDDD€ED•ź5x>;•@ń X¾X"ŽÉĒóqvøó0{u,kK}˜?žų b"ėš~y6ꌱjG‘£]Ģ”1ņõŖŚŸ„Š.ę„k.ā²- žP”w0ø6‹—×2æĀ=Äõs(\_ĖĢRGĮĄ£SĻ /ÆįŅÆB»>Ü“gUšU‰ś\DDDDŽ^ZDäPmĄóÕłų:¬@Ūążɬ)õsrēŠķ/©ņ0a~"—°“ų(¦'@ ÖKhŚuøV7ūDµ©ˆˆˆČ!änšČW>Ęģ–æć¹»‡“iF•3ƒ¤å䐗‹uX¶žĆÄń<°Ä&ģ€i™$Ä{i×,ŽszĒÓ5īą6fuq’ś¶’ļ #l² ¼tj—Ģ‹!Å©ēĶqXÖ#‡cRõŒü¾±÷īZY½cÕ ÷IyÜ×ÉĀŲĶ~`yL|“odx÷ś$€żĄ0HKö’g¦Ē‚ˆˆˆˆČ””°ˆČAęŗÖ“š¹ÕX¾8&-ć›% T×[\Ųo#Ę÷:śµ©bįŗŸŠ„Ž9uœÕ³ ŸĒ"—JŻ“ė‰ž4‚MŌø""""‡„Ćš c™éI"ęū±|ød(æmē=|Ŗg6åŌ?ßĻ©‡ļ1ÕµvV °Ź7…ųdz7®ŠpĻ©ō:HĶéÖÕšč{%ĢHˆē‚!É4 Ą¦²z›&q†öģĮ“›Ä}żbšmóZ|²‰±»ż  >Ū”tS=STpĒ«Õ Ž„?¶²~ŚPu†ÓOĶātu‹ˆˆˆˆüŅĻÓÕ""—UüŽŅ)xā2¶¼–e@Ū*Śeגk’č=AŸĆÕC‹YPC(la™ OÕ¦ŸĒÄYšNļ‡Ōø""""‡BhcĘŅåĀæpÜ·åå§óė¶}‰ßįrKłęłĒy}ś K+  ­E/Īŗü ĪhŪ Æē«WžÅ«_.`]­Ÿ&m›ć«vŁ÷tĖ™žŚ³¼öķbVWö¦Šļʹep †Sʬw^ä„ń3Y± Rš÷dÄ%æįœ®É Į2g5Æ^{_ōø—ē/iݐłø‡ņÜŹ)<2ś {O^ѕ˜CЌfŒ—ŽŁž†²ņbč“ęņ›7«ųxu ½Zżxł‚yømR…!OŒ—nRŻ7†4ĆeŹ'kłĖ†ž½0‰ęż°rŹ:®œąĮĖRéŚE“KBĢ«÷pŅĄTĪÉi\°y“¶+ÉeįW ł Ąąųłüµ­®ĶĢi„¼0Ǝ嵐’ä“)œ›×¤tm¦M.å•B¬¬p[żgs{ Ɖ2ejÆ. ±¢Ś%93–s¦pjęĪ£G63ą„CN`Æ÷!3ĘKĒĶĖ7 2ØS<Ē|“žæZB§¬LN ī¦]÷Ų'a^~u=Ÿ7ĻāÅžž†c!aāw„ügQˆua“¬,¾xõÉ&""""G1€EŽX.ŽecŃ:ęĶ›IuE)];µ#3»%Į¤¬ĘŒR£ńK~NĘāēš·{­mVšÉ÷ж 4Kmų]Ų†1s †wréž_ūćnęŚ18]n†@ŗXDDDä ŸwWLł˜Æéϟ¶„]r^ūūx&nģĆ©ēŚn5«,¤<’žtM+¼”B¦¾ū2ĻŻ’¹Ļ\CÆ@-3’u'|ˉ^ĒUM-J~ĪėK¶ W²dŚtвĻ榫:ļTcf'afń«wqĒXxÉh.oź²ņ‹’ņŅ]#|’ż\ÜŚ·“*ļEy8Ų¶ķü|³ š^?.õöĪ된ĻoOI Ķ%k*xjŅFž™‘Ė­­M:åš-©c^uĶćl®ąĻI¢Ķ6)¤V¼‡lÓfĘ¢:Š›ÉÜéøĄ­zfņ§± ą²hR·Ķ†ĮżÓ¹"V.,ē…÷‹Ø?'›K›@üŅIIüyH€xĒĮL¶0p™’mw.ńšė2ųC¼ĆĢ©„<łaM.I£·ļØ;D°—Ķµ†¹/—”¦‡”}xļ•MŒ_ehWg—ķŗē>Ł‘ĆŒÆŠ¹w±ĮÉ}3ø&JÖWóZ”Ą""""rtSX䈻¶vq£µ|??ų0ć?™@Uu-XM3RčÕ§=gŒ8›‘gĀ›Œ‰§įŠ\ąŸ….Ē(™¹Ó߯ŲhpĖūpž±.×7Nė»|üm”V\yĀĪo†y=>ģuŸā¶<_,"""rPOĄ‹ł|ül’ŽM׀ē˜“œr;}¶ŠSĪo¾Ķ<£1yčٵ5éšŗY7Ldśr›žM§2ę‹2Ś]x'×žŚ¤į̼cĖ?›É÷ŪfĢļŹ±][oYÆ[=·?* ł¹1zd.&еc6u«Góö{3ųÕĶ}‰Ż±ŹU{.ĻHčĖM’ī{Ø/fG]LŪ”¤,Ä'ßU²Ņą“œś›CæĘ·Ķ°XłĆ:>)Œ`·öŪ4HW³„É«mNėdaDėYP śl{>ž”ĄõC#Ü3q—,÷ѧ}#»ÄqLŅö™ø¾XĶÓ¼[^sCµ¼5'B‹csø¾›č–ć„¶doͬeԈŲ-ķ›Ćqyž­}Ŗįķy6½†dqq›†yp[‰2ķÅML\ēŅ»łŃum^¾‘ÓŪøåg+%‰'/N¦•ć²®FĮ ąßõ:Ģ$?­ü0½,ŠŻ8ōŪuļūdĖ.WWĖū £“ļ›ĖuŻ= ż›k²üūZčÓMDDDDŽb¦š@äČŠpŃģ ×ņČß┓G0ńćq¤˜Ś7‰'7Ę"+ŃƒłĖ¹ėūę?RU“gėm 5ä”ī·ŅyxĢķ?jmžžhpß’ąwÆBĆ6[oe'A¬žūn~׹” +K¶æIdzc0Jfģ[]ŹĘsmļvōæķkŖ©gāŻČō ¢ „'r}ēfüźßE8ūKŁѼõoS D ųģ”+ѧ#-šµ£÷é£ł÷ÜŹĘ½ŹfłĖ—Ńæskšę6„e—Į\üĄnNp6šÅƒ—qj’n“nŚ”¼Ö=¹ā­ULųc'ņ†>Źā-£`Ū,~d(M{Üʤāmė+"""ņó°WOd²{õE®ЌüŌ ©A Æ ńńń¤$§ą&ę°,¾_L]Č=wŻŹŻ</‚‹AĆPf#ø*d3me ÓWÖPX”¬&ʦŚ(ĪQvwĆ4 )č!%ÖC“x‹.Y&-“v}'ĆØYéŁ~Œ·ŗˆĮŪ3 “ĘĄ0`DgŽykC%ĘĄķ#\īg0~AC™]r”yŚ6õ°¼P½rŸ÷$;ŗy˜??Żūõ$šölę”»tJ7ˆ.Ī¬ņ…3ēRib3wś\Ün×Ó;®–©w_ĄoßĻå’žö÷e•óÕć·pū„wŃōė‡o’Žė"n{źJ²”šŪ§¹żžßsWĒÉ<=2Ć-eö„OYŻüžø’X’¢›0[ęŅÖ{¾cʆ?Ņ.Ė·Œ™Ó—Óū:ŗx^ž¼ĆŠˆˆČ/]„EŸ}ÉŖś"^øņL^ŲīDƌOę^@§žŸšŽCćł·»ßĻcīūŪ~ZyķDn2ˆ!Ęc’ė!3hģņ©t§¤‚»>ŖÄģœŹĶƒ|$aģ'™“e ‹ćŚĒšŌ'ÕLŖ‰„Ú:6¦ĘŅ3~-āńб]Ū%rĘ“"ž8©”w[dsiʁl÷m “rRĪĻÜžŚ+&öč Dš~­2ż?šøcÆLéŠŲކE3¾ŻU—׳“rRn̉ē½… K¹ä¼tŒŠ¦Ī5éqkObS2¶«ÆˆˆˆČ!šĻ§_—Ńī×wqM߄­ēÓnß<}7c?›Į=śæ‡ÕX¹-iéĖģŁk‰¶k¶Oā ļĒüE8ķrĪ,ķõĢ’¾”@ó–äZ°c¼ģ§”wPÆkŚfż8Pø-·1T.©g„ąŚ>q\“Übķń-ā(ę“E!*VDČoKŽ/z š5 >©’‚ Õ$ąš‹[†¶’ż“ōT2æ Š“Õ0Ü0N”yėlé~rMvU“’|ä[Ė7Av{ļ/öĘK|J€®ŪŲwńą¬a¤JVųcø©µƒšĪŪu?śdó{fƎĶņé&˜ˆˆˆˆübčÜWäįŲžóā‹tΊ'Ž­#.>@|l Ēōću½xƈR³~%¢Œ¾īj®ųķļ9éŌ3HĶlJC ņĄ„£¶ĖKߕ0nŽ&¢ö/ūyźż |Ēł”{^Ćæ]×!RW‰ įҐŻė ēōŃ#’ąÕŪČ8!]žĀ³_Ļ!<¬#“æ]L÷«®!ų̾[nÓĆ7‰IĶ2øīņ’²Ø¶šµ£»“Żę5øŲƒ˜ā*\āYóń£ÜżŌ8f,Ū@? µÕ³~÷uHŹiżoēĻ㿤ō×琸p 3j;séńi¢MDDD~v5³'2©ŗ œŌ…VŪžø“āķ×¾dRE?NNŲĆyWܱŒ™ĒoßĻ=üšS:„ć«[ĢŗŠžĻ#øŽœ="—›Žś;.`H¾ĖŹĻ_ćõ5yœuUÆĶyŗ·å¹•Sxdō,<ö&ž¼¢ėnƒ²‡„a‘€ā55Lļą„gŠ\*łhj5ém|$šQŠv<µōŃŃĖ53KX_ļćĢ“½?Ź(ŽVšš÷.ó|d œŗSfW±Öćē¬ L/­Ó Ž[¼‰²hīFŁ 2(+Č9ݼ\?ux“š +–óß/æÜi»oŁ”˜ gwöpóō Üc&1,ۃ7eUČ˰~‚æōkŪŚóֆš9ååõLł¾Š/Ė,NžĘXvX7ūŽ'F ȹݼ\7mw‘ÄȾHˆ‚°>ßDDDDä覰ČaÆaßźŠ2ęĢ[B‡X—Əxæ&×ÅKĖ00\°„Ć€A\bACQ_ļ4’|`TÖŁÜóŃz¬«S÷ģ‰?ד1|;o’h] ‘0Žł3¾–§ax‚DÖ}CŻĢ1k׹KHŽł^a»`„ü“ŗ™¹ ŚŽ{ßųœy•U|9£9ƒn=“ŲIóž×k9Ūū‹r†pw[ĢuĮĢą¬Ē^ē]·żÓa›‘„³ųI®øņE¬ īāww%ŻXĮĖJ_r IDATø’÷T#…ĻÄ_FaĀĘ3éńĶ7¬o;’Aٚ¢^DDD~īÓšJ¦~1PŪ č“¾ć¹œA“ćśŅö?ÆšŁ·%œtŹžVę£ŻåŽų—łĻ’^ą®7Ŗ±½ń¤ēt¤_^pgź~Ś_x;wś_ä„wį– Hnރóžrē“ńż¤ņÜ-ł¶‡ĆĆŠć™ōy%ĻĪ„ēń‰üi°ĶÓK¹u–ƒc™ÄĒzi—dnÓ^­ŗ$p̬f7IdšNN#–I|moYAQƒį³ČĖ rՙɌLh8Ÿ=”*ó>)焱EŲ>½śś˜å„Cß&Üķ)å_Ó7ņi-$§Ēpž©üŗÉž®­Lŗ߄æÅ”óņ‚Rž:ŁŸE‹6) hļ'ų‹}ŅŃ 6ĘÄZ³‰[ŽŽ„é1IŠ÷Ņ.?‘»NI O²±Ē÷ļ{Ÿ“ļŪ„å¼4ƌ;¦ŲŲ–Ezr€)¦:‘£÷ģūÄQæs7_ś5üē‚ėāŗ YinĆ?p\×iųŁql\ĒĮqœĘļ6U›J˜8žµØČ.̚5ƒįĆGī×{`ĆźEōé{öĢć”ü’büÄ[‚¦…×gį±B¼N †pZsbN¾_B‹-gĮā•Œ:ļb só˜Xū™[QgsĆ[kYæIL»Žƒ ©­ä†Ž•sĢĒ6ŹęāŸt&Y?ś]“ŗ†źŗdźŽ}ŽāJØŖŖĄļ÷‘–œ@ŅŅūnś”@zʏŗ,Z]Mmā©8ŻļųIŪ`/ū§ y“×tgüų¦¼2įā’uC?éĆo¬ųØŪŪ|y[wą«[»m’ōQŻ×ÜÜļR_ž"'žļ7|xānj’c›-ĆļÉŃū™Ų¹s7’““ X–zühÓž˜žÄ'„aš†ibšfćw Ć0žm˜` ?fĆpųFćœõ[ž^†nϊøīę0®ŪD¶?1ńĢ+(<.‡;Ū[ 艈ˆ‡Ÿ­ė=‘Ćœ2€EŽ ¹6N8LÓŌ֖UóaA1k+Āōm–É™]ŅI±øø,+«åėyӈ»–_]{3mŚ4cåź5Ō×׈‰ć§£¶Ė}­Wšwø‰ķˆŗAŒ€anmūŹøńß&ó #TŌ\“Ó÷ś¼-’ć¹ńčÓ~ūŒX»¬·É€}«KŁxF͌“žćć»'°šäōĪsß…“üæ1“ó˜˜'ŸB»ūļåi§%×Üѹ!Ų›2Œ+Ī’ē=õ;®²®å¼Ž9ųj׳¤<ŸsGõ$¦U{Zø/šź£ļŠäōv¤z Y[µ—ł$1}øš¼fŒxözVW“į7¶ÄŚE}EDDDDĄ„hc˜Z×eќR>ń%š÷6 žŠˆˆˆˆˆl¦°ČĀŽDi›Ez|€pi5łmņ±ŖjŁXQĶĀĀōȌƒ@*‘śæ‹ ĶžDĻ!ĆHJŒ§¾vsx’½ō] ó5ģó¾±üŲYƉ†>Ɨ–øåå1ßDłf™ˆīņ­įˆĶā >žüČ„o­Ł‘nŌ&Žƒ}  YBŪÕÆgžŪ‡‡ę–0ņŌ ņOįŌn÷17z£:lž3Oææ¼ÉRīę”7n粇«!>›§ŽŹˆ³!¾óÕ‘Ÿ@`‘#€įZų “Įłb©';>– éā:6©™ ŌŌ‡1A,Åsϧźū؊l$£I*†i°fĶ2rZl»Ę}*æ*dóķŅŖ=,e’™CVøŽ¹„ζ÷`¬óēį“»šŗ©K1~E°sW_ąµ1õßĖĖ)½$&lß'¶ SēDyļ“0yY&YY‘¢ Ŗ–{°üLcG—óĪ £xd”‡¦}ĪćŃ~OgƎw‘ŸB`‘#é£EßS˜6w2IŃ(1>' n˜ėĮcxš%T[K$ŌFšz=`TTnbŻŗõtģvÜ~?me {LžµüjD6-g­bn©s`¶ū`¬óēb˜8½¦vŽ=Dæ}ƒ>]sč’·Ų]oŗ}{xčŪƃ±©[°ŽŚŠf8Ē?1MŽ’æ@żńRFė9`9B¤¶éJ||"õįj"†‰ax/8&ėź,<®—IÆæEfz:]ź£TĘÅY¾ž¬ü<2šd“™¹yųē}Ÿ’wśŹuĄ`zpŗŻAØhįŁ÷ˆY…?/Oj,†ĻŚnQ×v°+C„ו*6±[^{Ģ„GG毈ˆˆˆˆˆˆˆˆˆˆ4 ‹ö\\ĆĄ°ŪoÕZ"U%8¶Iu$BįbŌW’—Bs«š¼nCˆĘ9””ÕSõĆb¼q1¤¦ebZ›÷}ŸaµØ2²—K“МqźżŻųeÜūƒ ¦—^½ÓųuŪ Ķā bC ļ~½’;ø˜“č”Ī•=ćho­‹0õ»u<“(Ņ8äóŽė\ΧłĶ¹-}x½”Ս™Éł½óy¼S·’{ó7' śōĻäÜ~rā-|®CQa5ć&—0®ŠŽ:¤“į”[Ļt.éKó ”o¬į“6ņīŚ(Īn–÷m1ļ¬Ü.mr{!§ž1 Œ}Ļüݬ“:ŗ—Kŗ¬˜µžGFqp©­r“Žżrųs›ļ|½žgŖ,ŗ÷ŹąŠ‘éæR̬„dFŒ„tjZÅśˆ­Žn3ßļŽėtØ5ź·”S,¬®°h—ķ#¼¾”eŪmŃ,/HjY)}"āńŅ„K*—é'öķµ¼±±”MŚöÉįŽ®0qR/•B~ūT.9-’;kxµx×Ė\xj6ęKyeÕžt«é½qŅ{ą8õøŃz ĆÅ5¼`Ą0µū‹ˆˆˆˆˆˆˆˆˆˆČ>QXäˆ`ćbaxüŲŽI=6ń‰qŃz*Ŗk1BUµŌś‚x:öē‡IHÉL&/'‹Ŗåx|~¶žī[0ø2dļõ²įš0«J#[øF Ž3:y˜õåZ^_ڐu»|¢‡—¤Ņ?{s¢ 8Ģ.ØcńFØßó:×V³ĄĪ w¾Å’¾·q=:fø,ž"““:ՖÕ2cM˜µŗ÷¼šWĮä6» 9»µ5|SW·ˆ%ńūJŖÓ‚“÷Ö3nMtĻT»žY6gåČ6«Y …'Ź”‚ČÖįžó×G9晟³Š»XfĮś(gfśh¢D]9L(,rDhČŲ5 š'7!kš(füēQ*,h“‘@ĄcņzIėŅ 3KLf3bccųvĢ'Œŗą’żäŅSć<{ŽYōÕÜ(?[Ē[ÅŪ/Ŗµqm›±cV1³i#ŽIęŚó’9mņ:n›"¼«ub3}q Ńćé¬bIÓ i„UĢŖŚĖÖtŻż [DDDDDDDDDDDä°¦¼5‘#Ä–į HėŲ—u‘ Ӌkxencį÷˜•T0ś,j«*lóĢ#›•诋 :ŌG!č7·ū`±7Õ³Ö¶h‘Eåa ¶ł*Ł2Ś³Ćś5›xžƒÕüe¶MŪ®‰t°v½N€Ŗ•| 2“]^Ķ|¬Y^Ķŗ½?ŁšŅ>ĖC}i=EŲå!VF=tĢõn-ĆōŅ)ŪC}I=ėv³LĒlõ„aŠķŸ"""""""""""rxP°ČįĘuĮŲBÄė„i Ģg‹B„cžk°ŗ$BČņŅ9ńM•»»uŚøŃ:>^ęįc2Éņ×3ö³0»ŠĆ¦µHęœMU,©‚ܶ©œ›aĀ·5ŌŌWó޼0÷õŹā÷‘¾(5hÖ>•sRĆ|0±z·ĖŒJ óΧÕŌjÆ‘Ć„Ą"‡Ć·! ėN$Äŗ5«÷įʎżˆÕė ))*ā™ßžČŁ]ņX¼”Ž"ä‚x¼ŗviO0Ę”WUšź?Ÿ!5«)½ō'!!˜Ų$,µedc/ĘBīŻ<Ė4°Ż„ŲŚ|ūŻ:”Ę…§Äį G˜1%Ä·…aę}SĄßźŅ9æC:·kB8ŹŹ„%LZ"ōÓ·G2—%Zx›¢ 5<’i+œŻ­ÓĘÅełMĢéžI×ā2¾*ßuĶ"¶E÷ž™œ›`RS^ĖGoąÕ‚Ķįb—Å“×qW4‹{dń· lŚXĆ[nä"w·Ė¼=¶˜·‹]ķ³"""""""""""rŲPXä0ćŗĖ˜Ć÷óę±fu!“§Nįó/æaSU%`ąŗ.~‡õx‘ēŒ”YJ†'HėĢ4 ×ĮÅ Wļ^¼žßwˆü°Ć%ÅEX®“Œ š¶jENÓ|ŅŅ3ŲÓhšń‹~­āö˜ŻTɓoUņäŽæp¢ĢœVČĢi;ySa·¾\¶ļėÜŗ056‘UlŲM¶bu ž.„½ĖF2{Z!³§ķ®c~¼Œė8hōg9œ(,r˜1 “—_y—xL0Ąp€H0§®–€/ŽZŹjBż6‘p-N‰K =“P(D\B€’¼ü……ėˆ Ʋbåj"Ń0®c’Ģż†Õ01b]»vß«:e%z9­[ļĶ*’yĒō1hH.焹ƫ䱏ŹXn™żFY’ÓÓiŚ¢5=śõ'+7O…ˆˆˆˆˆˆˆˆˆˆˆģ€EC~æŸ÷Ž}—c;Ž‚‚u /:.†į.øšāßĒwńY¤·kA}ńʇų°KńĢļt=‰±ļ~Ī—_MaåŖ5dffP®göܹøøøäęęńܳĻćółöŗ^—ōMcé†zęŌž|ćŌóĘĖxco–uėyżæKyż0ė_Ƕ™ųńG|ōĘk„*7ƒßēŻ2s=°nĆz–ĶšĪŲW^¤E§®\påÕä6k”ƒCDDDDDDDDDDDvK`‘ĆTVVĒ)#GRPP€Ń0 4`€õŃ(O¼ō6ćósڧ¹)‰Ōl¬¢"aŅū³lm5µõ8QÓćańā•„Ba0 rssųß’>"3#sß>0,ƒ?Ÿ’ōoÆe]yX“ĀõõÅcY×§o½ł™™Mö«N‰1ŹćŽ’ž¼™ĄG Ƕyņžæ²|Žlb~\×Åi ö®»]Ų×Åu] b|/>ņq tīŁ[)""""""""""";eŖ DoMš4įĖĻæą/·ßN\\†a`š&†i`.&..˜`X` _¦×ĀšĖ⦛ndĀ'Ÿģwšw³„‹»ĻĢįW=’ńl.Löčóq²xęt<–Õäu]ĒĮvl×mų¾ĶæĒĮq\×Įu]b|^^|ģ!Bu ¼‹ˆˆˆül܍|÷ļæóų§ėqČś*Yšæ—y廢³>łÅSXäą÷ū¹óÆw²dńb®¾ź*āć0 0,L×ņąX^ Ɔ£a¤h’““hŽ¢sē~Ļßžv7~æ’€ŌÉcü¦:’¼ØŪĘc™æÜ@šŽl{ø>ÄŲ×_Ęēóāønc`·ń»³M x›ĄoC†°Ó(nX¾fS9_}ü?""""‡Jt1/]s—żkõNK¦NeAq]ć-ąÖ.gģc7sѹgsņ©gqõkˆ¬|‡?^š;›^»e¹rĖ™óńX¾\VµūåDDDDDDDö’†€9‚d5Éā‰<Į}÷ŽĒųńćłņ«Æ˜3w6kV®¤¢b`˜˜DÓęłtļŚAƒ3lŲ0ābć^½Ü8,‹«ŁL[YĆō•5¬ŲXOym”šś£3‡!Öo’ōŠ<ÕK׬DZ$%³bÉüݾgę¤ļØ­Ŗ$ąó5tœ†aŸ &x6ŲHnŁ4Źņµ&ŽĆvCEū¼&}ž)'Ÿy¶‘½āR½t/½<ŽļƧ"ā%!£)žĒļĻėNņžžå3ƒ¤å䐗‹µÓl–¼ł0’œ—Ē„£ļ¤s²…/=«>…œœ2ā,ō£’nŻZ¾}’]>üf?UįĤѼSFŒ:‹“Z'4–÷·ū`ōD8Ā×371fI?T8Ų>-rƒŒģ•Ȱ&¾vĶaāųXbvĄ“L¼tlĻÆ{ĒÓ!Ø£FDDDDDŽl ‹āāā5j£F:|źä·Ü.Įķ~1ż`Ū6”Pˆņņņ=.;{Źd|^ļ–Ÿ‚ŗ4dj.Š0¤·Ļ æ:)Ģȁa^ėcģDī© +–QSUMl|œ‘=p+¾ćńæž“™C8’ź ɏwŁTš?˜Ab÷&āh6åŌ?ßĻ©»,`óæßHzæ?pęqķ¶¹ČĢM>šŪS5—ēo»—÷6d3päÆŻ2«r5Ó?Ēć7Oažõ÷p}æTų©Ū} ėŖćŁw6šN„‡AŻ’¹.ÓĀŖ 3}A%¾UĖüaYÜŲęP]Ŗkģ¬$$`;”—×ńŃ“R®_å”ó’騻%"""""rÓ%ˆČ!°fłRLcūŪY. `\°L—^lĪ!+£!sś‚SĆŌÖ¹|6Å»żŹ\—¢ukiŁ®½VDDDdģU XP“ʉ—_ÅŁsx{lj[–p(’ w¼6‡¢Ŗ0ž„\ŗž| 8æi&ą¬ęÕkoą‹÷ņü%­œ솩Ū¬’fNyĄ¢ķ„Ošč€™Ütå{4żĖæø¶[„å^ą©·'³dcOb6}.¹›‡d4<J¾|”Ė>)aCČCj‹^œułœŃ.v‡€hˆyÆ=ĶūE­øģ;ÕĢ×ųzo īO»{nę‰gžĶ±]®£Ļ¶Ū­œĀ#£Ÿ`į±7ńä]‰9ؽą2wR ļVųųķ9M87}óV9”},ķĘņųe×4ćż6ß~½‘×V„Y[é6L²³ƒœŃ7™Ó³· ;Q¦L-ćÕE!VT»$gĘrīĄNĶ41Ü(_O,ᵕaÖW;„0HKröĄTĪŹ2·¬ĆŒńŅ1Ūß°ķy1ōJpøųƒ*&¬O¢cSceģe=]›™ÓJya^Ėk!%=ČiR87Ļjœ“ĖaŁü2ž˜VĆā*oŒ—>2¹„ƒC‡Æˆˆˆˆˆģ'Ķ,"rŁv”ŖM;ĻŽ ŗ ķįėėøī’…Čʰi cš.Wœę¼SĀXŪ|Z›¦IUŦ}®‡[6žk{·£’m_SM=oģFŽ Ym\ <‘ė;7ćW’.j¢—²7.¢yėß2¦°‹ųśń«Ž£ M›¶„ēš«yāŪbģĶ8…Œ»ķ\N<® ­óóČĶoOæóīāß/ßĻļFö„Cóf“ģ:”ĖŸšĘ¦-YĶ6Ė_¾Œž[Ó4·)-» ęā¾ ŠŽa}ŗŅ&?Üüvōq ĻϬlh„ķ¶IDDDäǬŒl²Ģ2fMœAqdgK$·?™Ėnų+>t7Ÿ‘ÅŹwćłÉ5ū0'ÆI““nąŸOżƒēžzŒ[NŹÜībŪYż!żs±'_ĖC<Č}ŐÖÉŪųüy}¹xō­ÜwĖe šĢā¹ū_bFŻÅŌĻgĀWI9įNŪümäÉā¤ó†’U5 ÓŖ0÷øŻ¶mc;‡`ęįh/¶Im—Äé;„5-/Ǝ‹#;TĆ'+l\lV¬ Q’’Č-gdrļˆdś™u<ón1ÆmŲ2ė2óæ-āνśgšŲØtFśėxņĆ2¦‡¶mUAˆ²“Dn9£ ÷H”æQĖ?ĒmžżĪy1ø„"{SĘŽÕsѤ"n›”yÆt8+sRüö~/5,ć”TņąµÄvNēŃó³yąäDNĢ“ü‘ŸDĄ""™ė6dśn¾‰“šäŅ»s”v-l:¶tHŒoœ+y‡{oQ"ƒ³N Ó&ßęÆł)ÆlĢpög~e;ŗł&ŸŸīżzx{6sŹ]:„D—NgVy„Ā™s©æ“ 1„™;}.n·ėébÖrń pę-Op[[—Åożū.ŗˆŠūc¹±›ÜM,™2…oć¹G;ć-ŸĶKwÜĖ-÷tęĀ›oåé?Ųń³Gøķ¾?šHĻoøėX/`’Žė"n{źJ²”šŪ§¹żžßsWĒÉ<=2có:Ūż‰§삿vŸ>uw^~­¾yˆŪm“ˆˆˆČYĆ}Ķī{ž.›Ś‚c aİAtĻ 4žŸÄ6ėNßĘåŪ“ŒcÕ7×1aIvæ¶{yŃląIlB~~ž– aw›ą­S¹‰ 7Žc:u¦}ĖŠr‡÷›Ä·:–z5dwJ.dśõ™¾Ü¦W§­9ĒNiu&ĶŪ“Ą·³ üf­h鍰|m1īŻo·‘Š—›žŻ÷ōS” Ķ›ųw^ļ4?­,XVÅiģ•`j€cóżX@Ļ|Ę ywfg Ŗįķy6½†dqqć°Ń­‡D™öā&&®s鯬a½Į”½›ś±Š-.ʌ7Ŗ™ŗĮ„wīõ‹:””‡’]ėc‚\žmą†Ŗ÷®ŒŻÖ³–·ęDhql×wóbŻr¼Ō–¬ć­™µŒ‹æĪ¦“¹1tČ0`§-$"""""²o9”ŗüķ÷µĢXĻŲĻćū…A‡u\xZ†¹5ˆ9óū ļMHĘu!35Āē–pśą’žĄ4Ģ¼ÆŒ”<1k˟Żćާ;÷1yF-aĆŌ©¬MĄ˜>™ł‘“ém,bŅŌJŚžÓōŖOøżßKépķē<ō›VX@æc[P½ųDž~ęS~÷ģHf6ˆmы}ŗį”Ł«>āÓ'Ū0ü¢ ō½`Ź»—0uźjģc[aaŠ~ ĆG³īŚéĻ,soĪZJtd¼›×ŁŖƒtĆC?śf­å«ļšŁüƒślæM""""?ę#oš5<Õļ\MžČ'ŽćÆcޤݯoā/ēv ΈP8å-^xw ŹØ÷Ęć­³±ŚEÜ9`ūaœÓm ĻŻž~8įdN=e(żZ&žx8éFfF™fUģ°”{Ą¶ūŠöĀOz\Ļć£W®Å[kėYēi^feÄ”hĀZ†MŲŗ˜mƒæĘŁiYf¢‡&†MEhėkįeńČĘĶgˤdĒsŻYÉō»pßĖųq=ėYõŠ7׳5#ÜōŠ5×ā?+ź)pbi›ĻÆ›ÖšĢ»,iĻé]ćéŸaķrߣ«Ė(5ˆČAž õxšx<8į(-rmŅ“¢tmSCūVÕŌG ‚Ąµa›¤Ž¼Ģzžļ‚B6VZ¤ÅŗxM›¼L?®ėz3« IDATāüōYڌŒŅå/<ūõĀĆ:2łŪÅtæź‚ĻŒį»å6=|“˜TМ!ƒ›į.{…µ9 ė³5«Osś—ÅCŸĶeEt$Ż~tŃ"3+£¦”’Z °ŅÉJ‡)•7ͬžųQī~j3–m ĪŸ„æŚĘźYæĖz[yĶČ3Ė)+WÖÆˆˆˆģù?GŃaੜžöŒ~퟼×ūQ.“>ąžĒaž|97^ъdc==ś“dįŽ|ĪøćzĻł‚?ų‡GæĻż•ūFµĀæ³ŗš;śb¦d“ć0oé ĀC»žč½ŃÕĖYń“›±%ąø«ķ¾øÅ” 1šq^r}0·Øžpǘ×»$Ģ rR<˜['پMv<×4,†œŌ„ó3·’EL¬…±“u†l;pŒ7/™GńGźyć£Rfś|tNŻ:Gš¾–±³zīńŒÕņq晹ō^]Ķ3+yšµ Žėׄ{ūwŗoˆˆˆˆˆˆģÕu˜š@Däh¼ ®Cll„kžĶāźg²Øø8ąnżJK sė«\ūÆ&,(šā±lž­·Lóܰ3s<“„_}μŹ)|9£9ƒN=“Į]WńÕ×kY’Õ,ʉm=€»_Y^ÆƵqÜĘw>¼ĒmȚ°?ĆW¾Čŗ.ך7ĘšĮĖw0"w÷é(†åуķh·‘ż ’˜ŽdøÅ¬+vÆZĘ*·#ĻL÷Öł4kيœų½øTŽ×s#@v÷SøņĪĒø÷ōDż”…Ń}­zNģŸJŁÄwųhĶ“ŁF‹ųģOY׋”½v2‡ģöŪ}HyœŌÖ¢tqcKw8«t¢L˜ZE?ȉ-v1÷­åūBŖl¬$ł–ĆņMā„é6_éū55üZ¤ūh•Ļu§$ZPʃ3ĆDŁĻ2v¬g²Ÿ–ž(ó ¢[w'ʼu6t?¹[ӂÉÉOą’ĪŹęc,Ķ©ā{[GŖˆˆˆˆˆģ?e‹ˆž@€ŗś:r\pœ†Ŗal}m[īÖ×\Ē\¢§ńW.¾ĄČ°hqŹ)“{ųMŽ{nß„ ąŹü&ÄnĒŻć_ēUkŁĆo„“<­ŗŠ1ę¦L^ƒŻ£eCpt%S¦ģŲ…`?nRÕ/šĖb§7Žx’ pāi‘¤g“DDD䡉,~ŸĒ>«§}—–d%śpŖ ˜öįÖśŪqz Žź|r܏ųߛIŠ”D«Œ¢šŻ<śfʓļR<ļ;f¬Ļ¢w“=×Į)œĪGó]š5O#)aīŚˆO ~Ÿ‡aŽ”ūEWsŚ¢ūyžOfłiĆ8®U VÅj¦OĖ'?8įŗKhģq»ŻŹ)<2ś {O^ѕ˜ƒŚ &ĒōMćŒõÅ<ūV!ĖŗĒÓ7ӃYfśü Ę –Ā 1[O‹7.Æąµ¤8:ÄÚÅå¼¶ŃĆš1ÄÄ9»³‡›§oą3‰aټ‘(«B^†uš܏s’¹±g×N-彖M8'eļŹŲm=AĪéęåś©xÄ›ĢŠTX¹°œ’–xłÕą ±€³©–±Š<ĶCĄ‰2§Ģ€µū†ˆˆˆˆˆČV Ė’oļĪä(ėüžźī¹'™™LŽ™$ä„$$®ˆ.CBŃ5D×]õ·.¢«FtAAK]Č.BH ČĮHČINf2“9ŗėłżQÕŻU}Lf&sēżŅ0]ÕÕ53•LĻÓϧæß@((,Ņ~Iń„•Ü„—$ō_—oTC“£IµĶY$ޤ?µI›vÅ4}l“äJuū¼q­µ*..ķš×`w/֗?üe½pömśŪwOU™¤Čøy:śZtÓVMųĀÚuäœs®&’ąūŗÅ +æ5Ż[‡wš9ŗā×E?żœ¾Zś5}t’Õš’ž”~¶ī]¶č,żßN\—‰S4ŽŽ”{ņ€Fœ?YC¢[õn½=Øļ 0ZP<6He{Õn{HŪßo‘)©ŌØĆOŠē®[ ¹ĆĢščkWģÖ-Ü”ki-RyåHMŖ)Ķ]j†iöĒĻ׳7’~łčL÷©G§ń½ļčé’¤Ū¶Ö©5R¦gźņ«/Ō„ˆ:\IlĶŠå?ü”¦üį÷zdɽŗįwūäUiĢ‘§č ‹.֜ɃåČŖłß·ź¼/=µØ†).Öē/©Õ”ē÷źį5{tżrWn,¢±#KõŏUhnM$Ō¢¬ĄqµrÅŻWgUVU¬łēVéG;©Ńņ1§ŽŠļŃŻÆīŅuĻZ© ¢ńGTéƒS:KF“>0Dó_ߦū–6höłeķśmFGž¾S:õ }éīėō…E’%4żóŗiŃv}óē×jįķõrc„Ŗ6Q3Ę–éģ÷bT<į ]ńĶ3tEŽcŠ4įœ+uć9Wę¾Ū£?ū½ĪYqģBżčŽ…©=įūż£†Ļ׏šļo]¬ėo½øē—Tzš¾żąiłæ«’1:ķŅÆč“Kāūt¢¾rlj=ū·QP Ł§ ÓģS|ģąq•śń¬Bå]ųĉję C5ó„\whį„cµ0ø«°LßżRņķ‚Ķ»h¬ęe>,V¤+>=6}ĶLŸĆ¶óė4wĀ0wBī» j+tĆ?V𣠠K@(-+“µVū›¤ÖĘżŠg·p.­*’ŪšP¬ŠńÖN²Žöģņ`'Ua'Z@›Ŗ9śéós2ö:Ŗ¹ō·ZœtužO™¢k{G×vĢū/½œY‹®«žē]•ž®5mĮ ść‚Ś}N ŗHæŽp‘æ‘ė{ąŠE = ¼¢R’“{_vliVķøōÓļ+kŌÜш!rŒT÷vL·•éāŁė‹z„MuMzkĖ0IRIi™"ŃżĀģæŻńķŲ¢³Vōƒo®æĪĒ’e’xt 0ō€Ŗ”ÕJø®š[#ŗ’Éįŗ²j»ŠŹ½*Ž#FķփK'hé‹#$IŅqqü&ŜVɕ­ ­x%Ŗ’Ū0X’T:h¢Qž¾Š?d@Ļ"A€P=¼F®ėJ‘ˆ–æQ©ńĖ5ļ”:G5²ź}]yĮ*mŻUŖęxTµUūTKH ÉMøzk½tēćcSėÜ6‚ r"€0ņ°1J$\ÉļÜü»eµjnu4’¤]*)wdŒT[¹7żWjm¶zõŻņčXķkņž®]×Õš‘#¹  '`čĆGŽ”q"©m×5zpyVo*ׅ'lՔ1MŠHĘ%āV;vG“xåP=žźPÅ&õøxĀÕØ±“й@(-+×'.æR6Ļŗo­±ŖS³ĘU4Ŗ šŠ–=ÅZoJTrŒ£yĒ„':ś'rA@NĄŠCĪ8ļüv7Ź’ŠQ—` €`€ €‚` €`€ €‚` ¢\č]/=ūŒ^~öi9ŃØN8żL1żh. č`čE+Ÿ~JŽśŸ*ˆEÕŌŅŖUO=©+¾õ=1m:t-  ­u…Œ±j‰Ē•H$äŗ ­^µ‚ :… `čE™3Dµ…-ڼÅÕŚõRćn«ĮUÕ\Š)ĄŠ‹Jk4sŚ>µNhÕĢ)Væxų07ėT. čZ@@/Ś[õE¶4jTŃ&9d“.ūҧ5Ø¢²’}#īfżõ»—ėkæYƄ$¹[õ—ėźów®ó¶@ €^T\1JīÉĻź”Š›ōɽiEõčžłø;õŅ£‹µ|cƒ¬$ٽZūōR½¼e淝©õ-:}Ŗf]·LMü3 Ė@/ŪŃҢϼż¾­Ö½|ˆüö)WĶ„ šX3Xž ŠeXzŁŚś½j,Œ)Ņ’ŠŽD³šqEųÓsd’>}ū#ś4żt##+ÉH¹;r=ęL=}ĄŠĖ¶·6Ė––h–ŽŌń±wµ/ŽŚ-ŸĒī^¬«gNÖ¬o.Õ>5kÉWŃčÓØWćž-KtĶō±ŗč®mr½Gh÷o/ÕøĆ?«‡ėzėīҬé‡ė°Q‡iĀQ³µšśĒµµ üŚŗśžŁ‡ėØ÷č­VI‰5ŗṉ́:å{/).IīVżł›—謓ŽÖcFkԘÉ:~ī•śåŹŗō„µ»CĻŻz•Ī?qŠĘŒ>Lć&ÆÓ/øL·æĻųŽ8t@/{/Ž,•–źŪCžÓå+ÕŠM°ä*O(įZI…šqŹń*zūE½“Ē‹Xćo<ÆU{ZõŚŹ—Õ,IjŃĖĻæ,{Ģ,Ķ,s4ō—ź›7߯?żåŗłsc“ęē_Ōw×°¢Č¶¼©{¾p™īŠü³næe&Är“Wėž{N;&_®›ļż­īæķß5'ņø¾żĻßђ}’Ō¤—nü¤>qżJ ’ä"Żõ»ßčWߙ-­|B«6¹ß‡8ÓVõ•8čg'Ē¢€žB hče;Z›¤Öøö9ÅŖvö«>ļž”yÕ\Ż“jnjŪžxŖfh‘ž}”Q ęė½åĖõné ™ēŸÕ+­ēh¦Y£eĖė4éc§h˜c™rš><Å{ģŃÓ¾”µŸ­ß­zCńyĒ)–ļ“&6ėĻ_łž¾óĪYŗå_ÕĢAmOH—N¦_ܾV¾°X7qŖb’ģĪwt›y(ē÷Ą!ÅÉŚvĘdŗjjŚ{  zĄŠĖ¶ļoԐĘ:=ŗQ·5@ļĒ›{f >ģC:ćØżzvéKj±uzö鵚qŕ:yĻ2=óVB‰Ė“lÓ81{¬"jцæ]ÆĻž÷A͘2I“»Tw®Oع©­ÆÕՖūžEW=T¤ĻŽü}=¬cær"£Ēj“³G»÷XÅß|IÆ6ÖjÖ©Gä›8„p^y7tł ō ĒØ€nC ½¬vė&ż*ś;mk-×Ņ÷Ę©¤®±‡~ŒŅģ3'kד×’Õ=§'^§ÓĻ»P³~GO.}W[ž|\kFž”³&E•Xū ]vłÆ“łØ+õ³ß>¬?Žż-Ķuą™ęŖ™ē錦õŗćė7jłūkĻl"1Eå*įJ6WBE¢¼r ų»¶Ć‡ń«]9 5y »@/²Öź°5ĻjnŁfmßV®Ó–¾&ó~}}öˆĘŸ{®&o~TŽög=SżA:f„N›=YÆ-žīżĖKŖ3WÓbR󚗵֝©O}õcśąŃ“5yŚŃ_‘ż+Ćֱ5*š¼@æųżuśŽ[ōĻ_øOė;¹¼qtģį٬•/l–Ė?”sĶH&øß(«ģ‚i7tłHŌ…‡”&ĻĄ“Ń(ō`čE»vķÖ[ļź®§&éž§GhēĪjhčž `»{±®ž9Y³¾¹Tūü}‘qótžōMŗē¦æjš9s49źhō9ējņ‹æŠ-+Fė¼ó§+&©`ā·ĻėŽŸ< 'W½¢W_}MļÖĀ^§RC*­6=ó'-Y_Æ` =ģ#śÉ/æ¤QĻ^§+žŠš:óĀ¢ś\}j~•^ųń•ŗö¾Ēµ|łśż=¢Õ‰ü߇Ӟ]L“ g¤ä¾Š7@/ڼe‹6Ŗē6NŌ[Ū¼§ä} Żöł¬U(œUd¬.¼ä$ÅģxĶ;ļHE%9cĪÕyĒ™£>¦‹ŒJ’¢Ó?ƛĶSäŃkµš¼9š3’‹z°~¢¦ģįŃŗčĖ—kĘ{÷źŪ÷®S<ćó–ĢøJ?ūŹT­ūéWuĖ«(6•:ūū÷čG”čé.×ÅæJ·¬¬SŌ8Š8y¾7e&ōĮÆĀ`ę ]5Ž4©ī3&c čOÓg]ü9¾Üś’·’µŽDŗue½r­+ėzŪ®›u]¹®ėLØ~ļN-YüWČcÕŖ4gĪ<.ĒH$ŌŌŌ¤={öč•W^ұĒßįs<óĢ2-^¼X‘HD[·nSccƒ&Ož¤ż×U4å"·użßų©ęœł;ĶzšI]{\Œ ŅŸ§O?F•••***R$į¢t×J- 54»jŽ[ÅV®Ķ’ֈä]Ķ «]õqÅ"FĒV¢’‚ƒą”cg©¼¢ZŽ‘q9ŽćŒČćŻ6Ž7i`ŒŒq¼l*9‰`ҳ†Š š^—…^·y?Äé×o6õśĶŗ®’śĶßēæf³®«ŗ½;õÄ_ž› €N;}īĒ4Ø¢:żĻq¼×tŽ‘c;ł:Ļ^ē™ĄK=^ļ@w"]€^‹yOĆƇÓI'¤{ī¹GkÖ¬ÕwÜ”Ė.»ŒApJ\ėž|§^°4nÄ`EźÖéo?æ]ė»DߟFų Ä]«Ż qÕļw½0؝’O1EQ£‘•15µŗŗåń÷ä8Ņ„' ŃŠr~¾€¾Ē(Ļ‹ō}F&ō|ąmQ–€®“¦+€ƒcĪš1m=Š€ •••ÉZ«AƒėĆ>G…ŗūī_kéҧT]]­‹.ŗˆ‹$I¶^ļ®ś«n}h­6ļl[:B“Oł¤~ńć/ėųB.m»āŚŪ˜h³Ņ·½ŠbŽ.8¶Bk·6éźßnŌ9SėŅ“«yiō5meĄ’÷īk½7yXćźgæü< +†£Į÷™ōj#*f`@ =‚zQQQ‘$©¢¢B’4{öi?~¬~ö³›t’ŸŃ+ļŠ—ÆĮå‡xŹi*uęµéĢkł7$¹VŚ^×Ŗ†ęD—Ÿ{rM‘ž\­[ŸxOo¼×¬oœ[Ó%m”tõļGÉŲpœŖü5F²žžmC €®‡¦ŽaXĀǘԘ4ćpĘ”ŠĆ˜É€^T\\¢„u4xšąŌ¾±cĒźŠ/}]å'ėĶMujj‰s”„øVڼ§„[Āߤ C µšäj­ŚŠ Æü÷»jjµ\x ĻJ®Ÿ–Ü2”I6㯵Z{ 8ØŃ§?¶LŽ3ż±g²1tplJņ ½ƒzÉßWlÖõæŁ ĪiZż^e辄/nSuU™®æj–†)åbŁ^×ŖęøŪķŸgśØb?¶Tv5ė{Ł""` ļȚF L¼…[ń™š!† 8ģ`Ō„†•&ųFĆĄx4sģÉHz0ō’}ūć:rÜ`v\­ĘŌ ŻwöIcõƒ/¢‘ĆʹPBö4Ä»µņ7ÓEĒUŖøĄŃŖ ŗ{ŁNž€^br·é…}MpŪ{@ź“ŖĪąåŗj|ź*€³Ē  u„ ¦Āćŗk@/9’CcņŽ7zÄ .€,q×jwcĒĀ_Ó¼Sļ>¤ŲŽŠŚķ2J(®Jµ–LSėčó•4„ĶĒ—8:q|©žX[Ƈ_Ü£yGUhHCH /3 8+ėÆl$k pšćĶä˜ŅސčW³äō ĢŽō»ā²¶}˜mQŃ«‹4x’oT8ŗDёŁwcæ/·ńu5æó+Õ5ĶTć“É-©É{®Y˵d]½šćVw=³Sל3‚æ  Æ0’±’MUŪt»v›\ŸĶ?ŠqdŒtęüµ6õG֕÷ŌŲēŸ%õœb˜ ›ßTŻKējļ“ß(1hrĪóU—GuXU6ģjŃSoŌėŠÓ‡©¤€*B ÷x©Æü¦wyɟuļēŽŪm½©7+ Ŗꇼ®¬kÓ·ĮÆ ūA²µ~qšó§7,I0@’AšŒńdphé§¶Ę?0ž¦—ńBߥm’ždõofūēŸF¤ĮŠs€ą ŠFŽć(‘H(‰pA€Hž¬°ÖOĒ4¶øĮŗ¾6æx†L|WNaźŸ[/ŪÜŖAš”óX§0ŖĮG;²/~Z{N|L6Z–óø‰ĆŠ“aW‹ZVĻ­oŠģɬQō‘‘ˆ’Ѧ2`˜X³’ŒYėś“{ެ\9’ćzXĒkķ‡ĄĘų­dwŽ`Ņ|&2$ĄżdؘUūŚo­œ• w3Ŗ~ćļsҾ©}É?™Ÿ#÷śæ€īE 7›Tų[VV¦śś:UTTra€ØÆÆSii©œ@»(XCsūÖžģ~Q…W¤|˜źŸ[ÆHy‘JN×öóYÄѠÛոīf5MżzĪcĘ-Ōß×x·—ÆßG ō©AIø t2”M?»¦»XIŽK޼GyU¾ÖC`ÆżsņQÉŃįóŠ^Šżh̘kgšĶƒÉ×ē©š7gœųzg‘I½†öĻŠĄApGŃhTC† ÕęĶ›U^>ˆ*`  ‰DB›7oRmķ(E£Q9m„Ū«9޾”„pý*š8D’TŽėŲqG9Uk×®épœ ‹‹KŌŌŌ¢Õ«Ws{ą8cŒjjj4räHž7:qÜ®ššŚŒė¼E[¶¤‹4Æ#_[¦1GN’$M¬‘~}«īŪ¤å/o‘µRy±tę £ž2\¶Õ;ŸŪŅ¢ÖQēh÷Ī÷B瓤ŚD6£õIDATڹŃ*Mę­|Ģa%ķž>tĆü\VčT pz~͚@*č <©æX8=˜]ėĖŻśė`2¼™ēžPčŖų <*Ų:5.Ķ<¹ÉĖząéž¬‹?—~[·æÖ“¬•µ’µ~›µr­+ėW“¹nBÖu½ 7וė&Tæw§–,~€+ ä±jÕ š3gč«WæŖķŪ··+N†æĆ† ×Ō©Óøxč7ö5»Śö~K»Ž-}īŸ4x’29‘tuƕ“§Aji•ŖĖ„h$škÕŠÓŽŁKdcƒržó‘—öźļkź$I_=§F§M.oók˜rģ,•WTĖq"2ÉŖoĒń¶ńn'ŻBĢ8ŽDBF;1&€ü²»$_Ó%źxēm$+|“w¦Ox=˜qŹōMB_€$T§krÜz]8(Tķ‹Ć­Ÿ  ·P ō Å‹’ĢEŗIyył+“įo4ÕʍļhćĘwøp˜bŸšžd*mć1e’žoEŽ»§Hšr„w{’ŪÆkńŪ\f ļń*ÓėĖßöö[yÕĄŹ(öMņī NŹY{GZ÷ Č!džpčėļæéہ-±ų/ō>` {ģń\ mŻŗ%oœ +++³ZčŠe·‚–‚!°äĮ~źküõ~ÓĶ½6ŠÉ„rœ+¹/S ŠĻĒ‘9÷ę=0³½sv»ēąšĮ¹ĻEõ/ō,`Ą€‘ v3C`Ā_Ą@ÕV,%?ųŃn0ö«{Sa°•?Į—>W2Īśœ“‚čļƒČÜ»sm™ĢūŚ ~óŒ  Ē”Ģ8y›š0På „Žąz0`Ą"ųŠņWKłĀąŠ=öĄPvųŽŠXŠū€> až“&”§ßjŅī†8ЧU•FuźÄbĶģ(b,胂oķ ƒóīęgąAvÉŲŠ7}Ą_ÖŗZ³=Īō _°’^ŪÖ*)¦łSx”ōuķ ƒó>š €6ǘ€¾‡č–¾¹_’ōosk4„¦˜ čÓVoŁÆ,ŽŖ„oĘ5J čGښØėx8 €CuģčŪ€> Łö™šŠY[¬]ūX²h˜ą``pø00ĄA 0 ĄĄ!ąčœĄE8ĄA 0€]sĶ5zńŹĄ!‚`{ķµ×ōo|ƒ 8D‚€C0Ą!„ Ų€~Āī|HŸ_„)W=¦z5é>W«Ā£®ÕKq’€ęGõŁŚ}Ėf¹Ž#“óĪy*|1… ˜€~ĆU<Wܵ’Š4óō“Uōę =æĖJ’āk—ił®½üÜJ5I’šµrŁ ²˜Ķ„äE ,Q.Š?˜ź‹t÷ʋRŪöŌ35S’¦'ŸmŠg/(ŃÖ§žŅ;å2ĖžŌ‹­óuŠyEKž~_Sž®–ļāĄ!ź‘GŃ®]»“{÷nķŽ½[[·nMmoŪ¶-u;O:U ,Ќ3øx@?D0ŠO™gėÜcµō±ēÕl÷jéćÆję5_×i»—č‰u %Ž^¢%&jΜ‰\,€ZZZŌÜÜ,IjmmUKKKj?€ƒ ` ærĘčĆs§é’ŻłW­Ś[§æ=;Qē,ś„Ź—|O÷?öŽÄė•ĆĪÕO§Fõ0W YóēĻo×qTž0ŠoEtąŃ“ļÜ„ū’s›žv¦®™0RƒęL××’x‡~y^£/ųfÄøR€ü~€…Š@?aw>¤ĻŒÆŅ”«S½æ/rųGuɌ ŗķśU9’M‹:;’BM[q£nxf¬>zɱ"’ä2uźT-Z“H7Žx#į/0€P ō#ÖZŁąŽČ}āÓŅu/l×G/>Śū’]|üæieüSZx?ā€0*~€tč'Lõ…ŗóķ 3ö:yŁ£ŚwYp×8]½“IWsÉæĄ””S°‘‘•d¤p5"ś”éÓ§kįĀ…š>}zÆ} &0†нØĄ~ō£q€CˆÓ”£M[UTt}ÕĖĻ/ē"z˜éä˜ĄĮh;nēäœa#2–ŗĢ+€8Ē|:9V$ūŗVžø½Õ'2 VŽŲäÜe‚ū²Ź5˜®@ޱ”1 'Mž$£Ją`äomڳ‹ :H;Ž8ȰčN‡aBüź fģ€ƒQUUUiTÆmŁĻÅōyÆmŁÆ!eŽļ®¶Ē&ÕEĘdŒ%t6gķŒ$ė’7ł!u12ĘČÉXįŌ‰Åzm[«®_¼U»öŹ €>mHYT•%QMƍµ1ō£_#“|Ć`ʒ"ž F‘@× T·5õf·2[ų%'ó˜ŗ:kīdGÓFÄd-׊÷Y+MƉéÜIĪʏ&ĻŲ±cćOķ®Ułę`Œd­WĄaw؟ż2Et^ÄX7Åč¼)%\ @?’ąhćDļ¦I7Œ9Pē–@§å.ŪČ親5L ń5Ź&źO&ĒF~Šį±eśpʓ@pھ۟  Vn¤wĖųė{k¼1c€ōøQžXѤ“©ĘŠĮ1&É/Šu²ą¬é·Ą„]ø…Ÿ b˜ø@z€šąćŹĢ1$#Jąą8&gp›nól‚ŪR`’.]ŃaŒĆ•@xDiœ@pöXR”ī2ĮT8x"a #:”Üšä’r­ćF €äø19647šŌh’ t“hŽ{Œd¬dS•ĄV6yŸM®ėęč82F:sž?ČZ›ś#ėŹZļ©}žY¼Ū©Ķō~žNz•ɼ•*ŽĶī毖ØöMżqū2Ś?§— V8X°—śzĮoz—·‘œ•³Žšn’¬¬7eg„A•Cż×•umśv ųõBa?H¶Ö‹”C‰ozƒōØp·e"ś©­ńL‡æé%A¼Š7p;Š9Ęūn’œćӈ488ł+€o26 9+Éȑµ®?)čČŹ•‘#9®w€u$keüŲ’£•¬ńĪLzƒ™Æ!čY&«ö7“ßZ9+īfTżĒßē¤+|Sū’2?Gīõt\“Ķ{3Ś@'ŚōōœMMZIŽ{K [æm“õĀŽTģµN>*Ł":|^Ń  §™Ü;ƒoL¶N…æ9ƒąŒĄ×;‹L*č5“ŗITņ&šRkņfµoĢfMh¾.Y,c%×õv%ė§ĒžIŒI·6~©Æƒ‰@€^aŒÉµ3‡[ByÆä­ł«ģŖ`łm čŁī9Wūēœ_€6µ] 3Ŗ€żł8/ō ?Ä+vüöĪN ź7]ń›lb%ęłś“¹iR­I}ĢØα6pęžÜÕæ €®’'ĪWld“9°Ķ#k]:/YńkÓZ@§ę“‰r I¦ū3@ļ097rTå&+‚3B_łĮĮū²Ö „É©’]"‡Ū@Ćßdå®É “„æFެ¬æi¼vĻÉ5­d•¼mÓAÆĻ€>ĄCŚäķŌ;łŅ•½~[č̐7;üU …tž‘ķŸ€Īi»“QŗżsņæĘČX›'NņÆį³Ā}£m`=ąĄśĮY ©č &»÷söĮź]ž˜Ŗś µŒ6įŠįĢĻFÖ t™6ątp› ~½Ā^›KkūĒ&[@c¼3%[@Ū>K:ą<#zW2ßUF čŌķģŖąTPœžšŒ ˜ ŠUBpvčT pjSÖBąPAoš ąIżĶĄ:ĄéÄģZ_Kõ/@Æ2ˆfsµ‚Uül_fž<ćóŃž蓬 ą|!pŖ“ńj}½š×[XÆÓzmę'H d\l³ļ@Æ Õéš÷…ŚA Uū†ĆāpėgĀ_ +EŪwXF,łŪŽ~빂`3c=9«`ļhCĖ?€¾Ė䯇¾žŽpš›¾Ų‹’Ż#gœ],C`)»8'MÕõz-£³Ī,öŃż oČ]ˆkņ˜ŁŽ9»ŻspĶąÜē¢ś8xy+€Ū „ä?Ś Į~uo* ¶ņ'ÓēJĆYŸS$Ą}Bž06ēźĄ&󾶂ß<ć@Ā_ K“Ł:o,å ‚GŁąį6u§Užf© }‚iĻī\k›\›yOHų t®œ;–RmœĆy°Āap(-żfY O2ķÜm²w›Ÿ‡ščZŃö”œ˜Ė[ ,ąP,š?ē/żåo Ļ2ķæĒ“óqæ@·ˆväąüÕĄR¾08t=ĄćŠ?˜ßSčю> 8a×®08ļnĖÕčW:Üś=#z0n_œ÷Ń\}€ŒŠčyŃ®:Q[|‡Šņ}K“'> ƒŠżž? „į©WÆćYIEND®B`‚gnome-shell-extension-gsconnect-50/data/metainfo/meson.build000066400000000000000000000006651421543444100243730ustar00rootroot00000000000000# AppStream MetaInfo metainfo_file = i18n.merge_file( input: app_id + '.metainfo.xml.in', output: app_id + '.metainfo.xml', po_dir: '../../po', install: true, install_dir: join_paths(datadir, 'metainfo'), ) appstream = find_program('appstreamcli', required: false) if appstream.found() test('Validate metainfo file', appstream, args: ['validate', '--no-net', '--pedantic', metainfo_file], suite: 'data', ) endif org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in000066400000000000000000000110001421543444100340040ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/data/metainfo org.gnome.Shell.Extensions.GSConnect GSConnect KDE Connect implementation for GNOME CC0-1.0 GPL-2.0-or-later https://github.com/GSConnect/gnome-shell-extension-gsconnect/ https://github.com/GSConnect/gnome-shell-extension-gsconnect/issues/new https://github.com/GSConnect/gnome-shell-extension-gsconnect/wiki/Help https://crwd.in/gsconnect GSConnect Team org.gnome.Shell.Extensions.GSConnect.desktop org.gnome.Shell.Extensions.GSConnect.Preferences.desktop org.gnome.Shell.Extensions.GSConnect org.gnome.Shell

GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows.

With GSConnect you can securely connect to mobile devices and other desktops to:

  • Share files, links and text
  • Send and receive messages
  • Sync clipboard content
  • Sync contacts
  • Sync notifications
  • Control media players
  • Control system volume
  • Execute predefined commands
  • And more…
https://github.com/GSConnect/gnome-shell-extension-gsconnect/releases/tag/v48 https://github.com/GSConnect/gnome-shell-extension-gsconnect/releases/tag/v47 https://github.com/GSConnect/gnome-shell-extension-gsconnect/releases/tag/v46 https://github.com/GSConnect/gnome-shell-extension-gsconnect/releases/tag/v45 https://github.com/GSConnect/gnome-shell-extension-gsconnect/releases/tag/v44 https://github.com/GSConnect/gnome-shell-extension-gsconnect/releases/tag/v43 https://github.com/GSConnect/gnome-shell-extension-gsconnect/releases/tag/v42 https://github.com/GSConnect/gnome-shell-extension-gsconnect/releases/tag/v41 https://github.com/GSConnect/gnome-shell-extension-gsconnect/releases/tag/v40 https://github.com/GSConnect/gnome-shell-extension-gsconnect/releases/tag/v39 https://github.com/GSConnect/gnome-shell-extension-gsconnect/releases/tag/v38 https://github.com/GSConnect/gnome-shell-extension-gsconnect/releases/tag/v37 https://github.com/GSConnect/gnome-shell-extension-gsconnect/releases/tag/v36 https://github.com/GSConnect/gnome-shell-extension-gsconnect/releases/tag/v35 https://github.com/GSConnect/gnome-shell-extension-gsconnect/releases/tag/v34 https://github.com/GSConnect/gnome-shell-extension-gsconnect/releases/tag/v33 https://raw.githubusercontent.com/GSConnect/gnome-shell-extension-gsconnect/master/data/metainfo/image-01.png GSConnect in GNOME Shell
gnome-shell-extension-gsconnect-50/data/org.gnome.Shell.Extensions.GSConnect.Preferences.desktop.in000066400000000000000000000003431421543444100336010ustar00rootroot00000000000000[Desktop Entry] Type=Application Name=GSConnect Preferences Exec=gapplication action org.gnome.Shell.Extensions.GSConnect preferences Terminal=false NoDisplay=true Icon=org.gnome.Shell.Extensions.GSConnect Categories=Network; gnome-shell-extension-gsconnect-50/data/org.gnome.Shell.Extensions.GSConnect.desktop.in000066400000000000000000000004661421543444100313470ustar00rootroot00000000000000[Desktop Entry] Type=Application Name=GSConnect Exec=gapplication launch org.gnome.Shell.Extensions.GSConnect %U Terminal=false NoDisplay=true Icon=org.gnome.Shell.Extensions.GSConnect Categories=Network; MimeType=x-scheme-handler/sms;x-scheme-handler/tel; DBusActivatable=true X-GNOME-UsesNotifications=true gnome-shell-extension-gsconnect-50/data/org.gnome.Shell.Extensions.GSConnect.gresource.xml000066400000000000000000000110411421543444100320550ustar00rootroot00000000000000 application.css ui/connect-dialog.ui ui/contacts-address-row.ui ui/contact-chooser.ui ui/legacy-messaging-dialog.ui ui/messaging-conversation-message.ui ui/messaging-conversation-summary.ui ui/messaging-conversation.ui ui/messaging-window.ui ui/mousepad-input-dialog.ui ui/notification-reply-dialog.ui ui/preferences-command-editor.ui ui/preferences-device-panel.ui ui/preferences-section-row.ui ui/preferences-shortcut-editor.ui ui/preferences-window.ui ui/service-device-chooser.ui ui/service-error-dialog.ui org.gnome.Shell.Extensions.GSConnect.desktop org.gnome.Shell.Extensions.GSConnect.Preferences.desktop org.gnome.Shell.Extensions.GSConnect.service.in org.gnome.Shell.Extensions.GSConnect.xml org.gnome.Shell.Extensions.GSConnect.sdp.xml icons/org.gnome.Shell.Extensions.GSConnect.svg icons/org.gnome.Shell.Extensions.GSConnect-symbolic.svg icons/computer-symbolic.svg icons/smartphone-symbolic.svg icons/tv-symbolic.svg icons/laptop-symbolic.svg icons/tablet-symbolic.svg icons/phonelink-symbolic.svg icons/phonelink-delete-symbolic.svg icons/phonelink-lock-symbolic.svg icons/phonelink-off-symbolic.svg icons/phonelink-ring-symbolic.svg icons/phonelink-setup-symbolic.svg icons/group-avatar-symbolic.svg icons/sms-send.svg icons/sms-symbolic.svg images/enter-keyboard-shortcut.svg webextension/org.gnome.shell.extensions.gsconnect.json.google.in webextension/org.gnome.shell.extensions.gsconnect.json.mozilla.in images/chrome-badge.png images/firefox-badge.png gnome-shell-extension-gsconnect-50/data/org.gnome.Shell.Extensions.GSConnect.gschema.xml000066400000000000000000000130671421543444100315000ustar00rootroot00000000000000 true false "" "" [] false true (0, 0) false "" {} ["sms", "ring", "mount", "commands", "share", "photo", "keyboard"] "" false "smartphone" [] [] [] [] "" false true false Enables custom battery notification 80 false false false true true true true true '{}' true "" {} true true "" false true "lower" false "mute" true true gnome-shell-extension-gsconnect-50/data/org.gnome.Shell.Extensions.GSConnect.sdp.xml000066400000000000000000000021661421543444100306550ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/org.gnome.Shell.Extensions.GSConnect.service.in000066400000000000000000000001431421543444100313260ustar00rootroot00000000000000[D-BUS Service] Name=org.gnome.Shell.Extensions.GSConnect Exec=@PACKAGE_DATADIR@/service/daemon.js gnome-shell-extension-gsconnect-50/data/org.gnome.Shell.Extensions.GSConnect.xml000066400000000000000000000052201421543444100300620ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/ui/000077500000000000000000000000001421543444100210355ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/data/ui/connect-dialog.ui000066400000000000000000000120611421543444100242620ustar00rootroot00000000000000 1716 1764 1 1 gnome-shell-extension-gsconnect-50/data/ui/contact-chooser.ui000066400000000000000000000117521421543444100244750ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/ui/contacts-address-row.ui000066400000000000000000000063011421543444100254420ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/ui/legacy-messaging-dialog.ui000066400000000000000000000210611421543444100260500ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/ui/messaging-conversation-message.ui000066400000000000000000000060301421543444100275020ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/ui/messaging-conversation-summary.ui000066400000000000000000000061531421543444100275610ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/ui/messaging-conversation.ui000066400000000000000000000114741421543444100260700ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/ui/messaging-window.ui000066400000000000000000000330011421543444100246530ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/ui/mousepad-input-dialog.ui000066400000000000000000000171771421543444100256200ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/ui/notification-reply-dialog.ui000066400000000000000000000167301421543444100264570ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/ui/preferences-command-editor.ui000066400000000000000000000166261421543444100266100ustar00rootroot00000000000000 application/x-executable False True dialog command-filter True Choose an executable GSConnectPreferencesCommandEditor False Cancel True True Open False True chooser-cancel-button chooser-open-button gnome-shell-extension-gsconnect-50/data/ui/preferences-device-panel.ui000066400000000000000000005016571421543444100262450ustar00rootroot00000000000000 80 99 01 1 5
Encryption Info settings.encryption-info Pair settings.pair action-disabled Unpair settings.unpair action-disabled
To Device edit-paste-symbolic settings.send-content From Device edit-copy-symbolic settings.receive-content
Nothing audio-volume-medium-symbolic settings.ringing-volume nothing Restore audio-volume-medium-symbolic settings.ringing-volume restore Lower audio-volume-low-symbolic settings.ringing-volume lower Mute audio-volume-muted-symbolic settings.ringing-volume mute
Nothing audio-volume-medium-symbolic settings.talking-volume nothing Restore audio-volume-medium-symbolic settings.talking-volume restore Lower audio-volume-low-symbolic settings.talking-volume lower Mute audio-volume-muted-symbolic settings.talking-volume mute
gnome-shell-extension-gsconnect-50/data/ui/preferences-section-row.ui000066400000000000000000000056661421543444100261610ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/ui/preferences-shortcut-editor.ui000066400000000000000000000133501421543444100270340ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/ui/preferences-window.ui000066400000000000000000001166021421543444100252100ustar00rootroot00000000000000 False True 10 6 6 True False start Device Name rename-entry 0 0 2 True True True center center True name 20 0 1 _Rename True True False False False True 1 1
Connect to… win.connect
Display Mode Panel win.display-mode panel User Menu win.display-mode user-menu
Generate Support Log win.support-log Help win.help About GSConnect win.about
gnome-shell-extension-gsconnect-50/data/ui/service-device-chooser.ui000066400000000000000000000134671421543444100257440ustar00rootroot00000000000000 gnome-shell-extension-gsconnect-50/data/ui/service-error-dialog.ui000066400000000000000000000176401421543444100254300ustar00rootroot00000000000000 expander 0 True gnome-shell-extension-gsconnect-50/data/webextension/000077500000000000000000000000001421543444100231325ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/data/webextension/meson.build000066400000000000000000000024561421543444100253030ustar00rootroot00000000000000# WebExtension Manifests if get_option('webextension') nmh_manifest = 'org.gnome.shell.extensions.gsconnect.json' # Google (Chrome/Chromium) google_nmh = configure_file( input: 'org.gnome.shell.extensions.gsconnect.json.google.in', output: 'org.gnome.shell.extensions.gsconnect.json.google', configuration: extconfig ) chrome_nmhdir = get_option('chrome_nmhdir') if chrome_nmhdir == '' chrome_nmhdir = join_paths(sysconfdir, 'opt', 'chrome', 'native-messaging-hosts') endif install_data( google_nmh, rename: join_paths(chrome_nmhdir, nmh_manifest) ) chromium_nmhdir = get_option('chromium_nmhdir') if chromium_nmhdir == '' chromium_nmhdir = join_paths(sysconfdir, 'chromium', 'native-messaging-hosts') endif install_data( google_nmh, rename: join_paths(chromium_nmhdir, nmh_manifest) ) # Mozilla mozilla_nmh = configure_file( input: 'org.gnome.shell.extensions.gsconnect.json.mozilla.in', output: 'org.gnome.shell.extensions.gsconnect.json.mozilla', configuration: extconfig ) mozilla_nmhdir = get_option('mozilla_nmhdir') if mozilla_nmhdir == '' mozilla_nmhdir = join_paths(libdir, 'mozilla', 'native-messaging-hosts') endif install_data( mozilla_nmh, rename: join_paths(mozilla_nmhdir, nmh_manifest) ) endif org.gnome.shell.extensions.gsconnect.json.google.in000066400000000000000000000004461421543444100350130ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/data/webextension{ "name": "org.gnome.shell.extensions.gsconnect", "description": "Native messaging host for GSConnect WebExtension", "path": "@PACKAGE_DATADIR@/service/nativeMessagingHost.js", "type": "stdio", "allowed_origins": [ "chrome-extension://jfnifeihccihocjbfcfhicmmgpjicaec/" ] } org.gnome.shell.extensions.gsconnect.json.mozilla.in000066400000000000000000000004231421543444100352010ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/data/webextension{ "name": "org.gnome.shell.extensions.gsconnect", "description": "Native messaging host for GSConnect WebExtension", "path": "@PACKAGE_DATADIR@/service/nativeMessagingHost.js", "type": "stdio", "allowed_extensions": [ "gsconnect@andyholmes.github.io" ] } gnome-shell-extension-gsconnect-50/installed-tests/000077500000000000000000000000001421543444100226265ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/installed-tests/.eslintrc.json000066400000000000000000000037171421543444100254320ustar00rootroot00000000000000{ "env": { "jasmine": true }, "rules": { "no-restricted-globals": [ "error", { "name": "fdescribe", "message": "Do not commit fdescribe(). Use describe() instead." }, { "name": "fit", "message": "Do not commit fit(). Use it() instead." } ], "no-restricted-syntax": [ "error", { "selector": "CallExpression[callee.name=\"it\"] > ArrowFunctionExpression", "message": "Arrow functions can mess up some Jasmine APIs. Use function () instead" }, { "selector": "CallExpression[callee.name=\"describe\"] > ArrowFunctionExpression", "message": "Arrow functions can mess up some Jasmine APIs. Use function () instead" }, { "selector": "CallExpression[callee.name=\"beforeEach\"] > ArrowFunctionExpression", "message": "Arrow functions can mess up some Jasmine APIs. Use function () instead" }, { "selector": "CallExpression[callee.name=\"afterEach\"] > ArrowFunctionExpression", "message": "Arrow functions can mess up some Jasmine APIs. Use function () instead" }, { "selector": "CallExpression[callee.name=\"beforeAll\"] > ArrowFunctionExpression", "message": "Arrow functions can mess up some Jasmine APIs. Use function () instead" }, { "selector": "CallExpression[callee.name=\"afterAll\"] > ArrowFunctionExpression", "message": "Arrow functions can mess up some Jasmine APIs. Use function () instead" } ] }, "globals": { "clearInterval": "writable", "clearTimeout": "writable", "setInterval": "writable", "setTimeout": "writable" } } gnome-shell-extension-gsconnect-50/installed-tests/data/000077500000000000000000000000001421543444100235375ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/installed-tests/data/album.png000066400000000000000000000015621421543444100253510ustar00rootroot00000000000000‰PNG  IHDR{C­ pHYs.#.#x„?vtIMEä PÕKĄIDATxŚķĮĆ łS_įUĄo“’ŲPIEND®B`‚gnome-shell-extension-gsconnect-50/installed-tests/data/local-certificate.pem000066400000000000000000000037411421543444100276210ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIFpTCCA42gAwIBAgIUYVlJk/6vssQfUOXo9SNb7NfxixwwDQYJKoZIhvcNAQEL BQAwYjEdMBsGA1UECgwUYW5keWhvbG1lcy5naXRodWIuaW8xEjAQBgNVBAsMCUdT Q29ubmVjdDEtMCsGA1UEAwwkZDJkMjExN2EtNDlmNy00NzllLThkYWYtNjhjODlh M2Y3YWQyMB4XDTIwMDgyNTIyMDEwOVoXDTMwMDgyMzIyMDEwOVowYjEdMBsGA1UE CgwUYW5keWhvbG1lcy5naXRodWIuaW8xEjAQBgNVBAsMCUdTQ29ubmVjdDEtMCsG A1UEAwwkZDJkMjExN2EtNDlmNy00NzllLThkYWYtNjhjODlhM2Y3YWQyMIICIjAN BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxsFITnzdsa4lhk8opxJ8lcvEWZvn WwhjJIiu6qFnzlg9VAnS0QrUUMxNxOVVhmeSSPlV3+r23h3y4I/Irzm8PBOcBM4A uvYZneP9+K/24edrCsdoAIcws5nI2JTvbqF5XowRKy4YX9EaIQS3EvYfKrjYDiiF 0rVmV57GotrC/ntElF5CZlKO2338FuC78lWhnKHd6vJ6OyciMvdz10BsfkLBd0ME 7v74n1P0w6jV40J1KORtdtXKRo2XHgDBiDM557jHJTiIFNhb5LLOfcinCXgSuVXf otfwsBuD1evRrnA3hY/k/LHYUNEE0HscQXmhrPA7BdrSB4Cc5lPLVyTQa3rCD6vQ j/RL6dAwNeiiW8lgC+l4x8QBvxks8JLpLzLO6LwBf0QoOBQOlksgFI4qa+vku39k GLHihWK7T0eNW5ly1v3siQfcX1Y2BSJ+mywS7rqWLjAFbBLmwjKErYIoOpOC316c 9Z7YhQ8r8CBf1f91h5uyxIZ01NWyqn91ncJ5yn9ivXBEdF/w0YAYvh9LU18sUYYK AJR4RmE7uFGBmXO638MCXxItxyX2kvAVjJ3zxQbuMnBKVYDh/9vywwpgfIx96e3A PIZMXQPDuFX362FwXRKsf5hWOZMA7Si9CTLRxz05z+OedMR0IpAQLOdAGm9ll3YD HYuuUOFDyiiYP5cCAwEAAaNTMFEwHQYDVR0OBBYEFMjbPPr7CRy6OYk9H/G8BAHj TU94MB8GA1UdIwQYMBaAFMjbPPr7CRy6OYk9H/G8BAHjTU94MA8GA1UdEwEB/wQF MAMBAf8wDQYJKoZIhvcNAQELBQADggIBALXCEPO2z4TghwbDkxK0YQiqokKDs1I/ o6j5HKYw4OhL2WMQPkT0r39bFLazW1EO2mrH9twRSJ9im0Hwrggmw91CNMdy9fuH uNCExw9j+lbJeyBEAZTk/SwruKArEdksEUF7Gpe8eBnJ8epJWfiN95pNkMCxd3Oj mQ7UhALjuDwBAiOrQ9/iQPNU0tubbgfSLC500ycaE8SmEpdMF6bNzURYZLsMaBdi j9/pTezujrch66KVEIjsCHXSguxBR4imZC3nK3x70ILHqd+bOFJNVn1xNfc8fpxy ftRhZphBNb/hN7dlDG2ZkFAXfRu7F2xGmuC3n4OHKOSAEOP3EmYP7YS77+K4bk3s njbjnwZWE9wQPfbuK7fN5Sf8mKUFVZhkPKT22ARvhmKpCjcEnwxLgWxAfpEX60vr 41oZwnXbZwBKzSWhtzo8jQA4otEBS7qO4THi6iUWRcj4Cz4ThzeV6RgWSQemUU0v v8PR/ob84uisQN0nwPRPgHmwSRIb3TgguibKlOCEhNMRwmlPtQZp33AOLL8oPeP3 YTkVajMbUpTvUogh8Cf0xhJ0mHicxgkPbo/tvnnVxkjyCUmyX/juEaQTEbiSk+ww N7W1qp2gz7Sfjr1z4N4QQybEt9yknMoKL1wL1uKSdH/oQcGr9vaZUtJFcSVGlMX5 qCyBnxGQDn60 -----END CERTIFICATE----- gnome-shell-extension-gsconnect-50/installed-tests/data/local-private.pem000066400000000000000000000063101421543444100270040ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDGwUhOfN2xriWG TyinEnyVy8RZm+dbCGMkiK7qoWfOWD1UCdLRCtRQzE3E5VWGZ5JI+VXf6vbeHfLg j8ivObw8E5wEzgC69hmd4/34r/bh52sKx2gAhzCzmcjYlO9uoXlejBErLhhf0Roh BLcS9h8quNgOKIXStWZXnsai2sL+e0SUXkJmUo7bffwW4LvyVaGcod3q8no7JyIy 93PXQGx+QsF3QwTu/vifU/TDqNXjQnUo5G121cpGjZceAMGIMznnuMclOIgU2Fvk ss59yKcJeBK5Vd+i1/CwG4PV69GucDeFj+T8sdhQ0QTQexxBeaGs8DsF2tIHgJzm U8tXJNBresIPq9CP9Evp0DA16KJbyWAL6XjHxAG/GSzwkukvMs7ovAF/RCg4FA6W SyAUjipr6+S7f2QYseKFYrtPR41bmXLW/eyJB9xfVjYFIn6bLBLuupYuMAVsEubC MoStgig6k4LfXpz1ntiFDyvwIF/V/3WHm7LEhnTU1bKqf3WdwnnKf2K9cER0X/DR gBi+H0tTXyxRhgoAlHhGYTu4UYGZc7rfwwJfEi3HJfaS8BWMnfPFBu4ycEpVgOH/ 2/LDCmB8jH3p7cA8hkxdA8O4VffrYXBdEqx/mFY5kwDtKL0JMtHHPTnP4550xHQi kBAs50Aab2WXdgMdi65Q4UPKKJg/lwIDAQABAoICAQCwa0f/Qx6VZHqyaPkws1wa qrAyygvl5d/6wchhQ7uckP5+5elW3EHxJiexqc7samqSk58CDtHp/rNjWL1Nq/XF bbKDIUfMrD24xHLel3KQupVtD+rk7RrxkIOSm0Cb9oCAx9tFdLj18+k5fbHzBrxL c59zkcyXZ6TcCXdPftauhEQvXiuaH5XmhkGJHRo21IOLQLJ2pZyRfP8CNluAqRKk UCTh838hlPiilCcitW6FNqxAC+KOJN5TGcMVQp6GgtHXOVCrXS6NMi7/JSfcxope AVK9Z9gF958Q8ptm+tc3+yuNRlh/ZG0Z7y5Sz7QY+hnkI6iAXecn+aVLXP2U8Hx+ GSc4iJsBzqixYCNBrtkfEBXeL6AR+9z6aLJsYVlxkI7Zbw1uV/wPI5YDyqG3pBLZ fdiqQNd99K6jg/nJdigCXY5KMBAmtj8Cm/B9hsrrhOqBCNEcQ7ES9mprM3YF+euW fNVMUXSnwpH6mFN6KiJnxUJdgzqF3OJiGPAu9JSVZHxH3IZ4gVvjLh5Zg/zKO48v pNvvNLLDg/kU4OZmq9NojRmUhhEITodtky1pysroD+5jJyg4CuE5ewl9eQvSY0o9 0TrA2dbFzrYjT7jW7CEZLZlTd0Sh48qS7KXo19rFSllBWRU686tkukb5JJAuSZf3 2FI/ZJGlNNIuMFQeaq/gIQKCAQEA8am/oZlAp+3ySPlkNwDZUvfbRJ2sAy/aK1IP 4o/368YpXiMK0wSY1o4jUZGRX0c6Y65b+3AJdJ/5hr8lxKq7Yjek1F20wdXo6qI9 wnWQQvvEw2PiYvXj2nk05+deOotDZQgfPAY9FiJG5ohlhePlM0Ndv2cDl1rZLG8X l8q5fHJTS1TGIIXQR/j3qitiRLBMwn4rOvS/JyNuCTJc3DLHETsiLB13wUWGqy6B 42vUlKi9vDRawkpDLyatLEMEKFhNadL/2eg8KB9je1vyz+W9YMxZJujamwfoze6f i8Br46RqI6xof68MagIHcSP2Bb44G5dAlOlH44TvDs036jzzIwKCAQEA0oveZVlB WXPh6He/mzpCgXChI1LELcBsD0wGXDdDu0hJFX2UXhUIdGr+bDc+mBbndKniI/To 5jXsk32AoO1Tfr/M3U8lpDqQSJqnteaQ2iv/TIs4t0MmYqbBJDwQhoBt6WgIk6p7 zqQNxpBF1tMaJgO29XxQwLvz1LKzraZycrK5MX3qmlgndK0gv2uh+41Vj6fq9+9/ CwQmOnL6GqA3XuqMWwCDHzKWuQMw0mlilDj4zEBZrnzSfV/ABlcZfUURY4O0Eamj A0Yw3k6j99tFMPwqkw/7xS+EFf0aRnPIFF9Ad5N6C6nfyDgbUQPTNH7VGzQIcheO Vh3tTAlnNaaS/QKCAQBG/Jj47B6M9Z1tCC0C5zHvaDU1k6c6jGzmusVFxQqLbHss VtjQIZKPu9LuG/d66F5jd403b7KnWnKevTln6sr+T+AQLbJyGdbATYYcwBHvSyuC Ra3zac1TmLUMxe7s/Yl/fQJHzIFXJhxzjW9dBBOImmpIVgc9B4exwLRKd1dDEgYb o7xLQ2NqMNz3VKUaDjuOCifCurAH3CVveCbE2/mTuy4PjVxnHngvgorO9hbM0EBj r3FVjyDrEc5eqRTokP+0bTGQneJF2uqLCvhpT0/wxjYN8up8Dbe5/jVJhO1sQhiX gAZ2M0JPRWdQOcMD7ttmZ2imFVxzndHnJCsfmGXHAoIBAEAD0syRxLLD7w3VSuaR YiMk8Xlh8s/OT4yfGtfy3Z8VrVLhabjpQDbVSSHx8hAf9qOb+2vfTOihwJpfcDp9 rgM9obYwGEvEmpXYn+FIhwYulmLZeZcOzZ71AIhZ0tRyO/jZbrInBZmge6fBudpF ORAR1RDyiULwYoRrCQJlNyr0eCY6GJhw8R4ifXB18zwejsMs1N4pbUEWM+FVkAGE cRFk0uPgVf2oTfdWpwNyk0xpvgusDRhmT0FbWXEUDmXuGAlfw+IS58NZFgahdm0n t/Pa1776/xvHBKwC1nhRP6YiB+HTbyoYrjecB4IsXYz6eyTYPzEhRF+encWenkjL qqkCggEBAMhz/VK+dTBoawGF5gHd7ppBmpuyvxTaFjy9ZkTq6FC5G4yZtSsOhtOQ ve1EF+7hZMkdUIV7B6EzTs6bXPgLaLHbpJym1gTdTGUFNWQvdcF38oSfqEmfBnze CbdbMtCY8+Izx1l6gfduz0J4yBQ0ME2uo8FMOjcWFQuqRRkDIE+UF+LFJuINRqT+ N7RMZH+UYT/a6H+jolqaD+N/qUZzz3fU+GGTwJUpoI+dIUL5kUcGjIg972/XoPRX 4gFWdEG7qKGRPd+IARhXNxfkJwHv8S89RfEOZL2f6LI3mOqHlwda14WTmH6ct9S6 258ZGWdYdv8bBRaaLpN7nTcu8+o411E= -----END PRIVATE KEY----- gnome-shell-extension-gsconnect-50/installed-tests/data/remote-certificate.pem000066400000000000000000000037411421543444100300220ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIFpTCCA42gAwIBAgIUOMN5eqYOJUP5/eKfKD2xrUkaBFowDQYJKoZIhvcNAQEL BQAwYjEdMBsGA1UECgwUYW5keWhvbG1lcy5naXRodWIuaW8xEjAQBgNVBAsMCUdT Q29ubmVjdDEtMCsGA1UEAwwkNTBkMmFhZGYtNDg5Zi00YTQwLWI5ZWUtMTFjYmQ0 YWJkZDkyMB4XDTIwMDgyNTIyMDY1N1oXDTMwMDgyMzIyMDY1N1owYjEdMBsGA1UE CgwUYW5keWhvbG1lcy5naXRodWIuaW8xEjAQBgNVBAsMCUdTQ29ubmVjdDEtMCsG A1UEAwwkNTBkMmFhZGYtNDg5Zi00YTQwLWI5ZWUtMTFjYmQ0YWJkZDkyMIICIjAN BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwma5h3ijVffMtvjCGQpaPsgpYpDg MBxH+gmSJrHCGLFl6oAOUWLCZL/b870/zhFolZ0ncGuyQRH/nnapbpMAzBFjO/B3 hmSa9hEEIqhpDYDMdAr7lJDP+DsoO1KB5NuawJhQDdtAdF7Gpb/GaQV8FFi7BKRo b9NfQVoMJ0h/TrgtaPaGgmLZj3rfHqtfuf7W6g0qy5gBM1Ty/FJ6ih4dK/SMOQj9 FbI6jdQXbwMiLLRiaUozXMNMoZiYE12MUoCUQDyeQAj3afqjPX4eqJ3zcTZyCtha Qy+1goVjCDLcm1VGKJWJEZ4P5Vii1+U74KfoRgrvXK8xCaVhewWSxzO5L4sPPPCO 4MEO4JerDqiIjEm2ft4JNtwKjaY9kOhc5Oymg/R9ylWbjizEgZdECXnqrjeqVmIm xOm++5Ov4ovl6gRjgwarZjd2EvK66tKuZiMNRRM++gKsIsNdzv87fwPforSTs7jG V9tdN4f0HAbDGCUs5VtDym+VBfEPiO6ujHM3w42yOPI7sMKOpkOA8BbrMzOp4zPa qBdssQvqW7T0yWqPHBB92NFBI61j4NBS5P5QWcPoOAPTlopvoASdv7Ol5gnzi8o2 mNbc6Z3Prr7PlzRcYSLEqqduTwjL+BAH83B47xH81GUL0ORO3SpKp1iA13y4nzFC JUu+lVaVVKgfuKkCAwEAAaNTMFEwHQYDVR0OBBYEFP0VzjfOBxXa98JtmYAVD3H/ 1PMpMB8GA1UdIwQYMBaAFP0VzjfOBxXa98JtmYAVD3H/1PMpMA8GA1UdEwEB/wQF MAMBAf8wDQYJKoZIhvcNAQELBQADggIBABCXawCETkwtTMMcOtw1RjxqLupv9tmZ A6Y8dRtwaalIFxnwmki7nNbWjI1xIvkGA/gFmNUsKcCyCVaQxgTevmlOaSj0/v0/ kUmyOVbhb+HUVC4swBL5R+EirBREwpZF712KZdirdeqemtO1l2lmqhLYo3dHnbiX ZJguVN73ccAT2B+v+q724BLN25ztVXDLAT043uOCDr4uRKtuJ7pOhEfyjJOJa4J1 nScl07UVoC7WxMeeMtErtbZmY7FOhmjVGs/a+uP+7LME5APE3h3oYBz9LgwUvNaD 7HsAlHO3aTuRFE7T0AHmFghBsL7sfNqyEeveuvmZNOUW9Xj8CRZ94cvn7pucElEs 9uTbr6q5WWIXzIOJAiXMevtXCuNixw2S1U2prahJ5vUU5iSiWynFsI7+w8aLr7Oi 2mdqKd4a7QF0kwMhqxWsdbTWvhLugOX6MjYyZ9iXwg0LFOS63wqkMrIQgcf+rPXL jQ38b+xdl/OMDvfozeOnKXAxWFwLiHqFcIGrp1XJKYEYbDE3PK3sS7tPZ1goM+1S tarfmqhtMZ5FLAjPP7QJ0v82b2Y+PFRkbkFqn3HAbCcjgBEODTmH4W+VoqQAViW6 Ghu8N3E0hCigwybhy8/nM4kXPI98ROoQGXpOJnyeTAODXcAvv2ecmunh6J/NqcI7 cmX79mp8S+vH -----END CERTIFICATE----- gnome-shell-extension-gsconnect-50/installed-tests/data/remote-private.pem000066400000000000000000000063101421543444100272050ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDCZrmHeKNV98y2 +MIZClo+yClikOAwHEf6CZImscIYsWXqgA5RYsJkv9vzvT/OEWiVnSdwa7JBEf+e dqlukwDMEWM78HeGZJr2EQQiqGkNgMx0CvuUkM/4Oyg7UoHk25rAmFAN20B0Xsal v8ZpBXwUWLsEpGhv019BWgwnSH9OuC1o9oaCYtmPet8eq1+5/tbqDSrLmAEzVPL8 UnqKHh0r9Iw5CP0VsjqN1BdvAyIstGJpSjNcw0yhmJgTXYxSgJRAPJ5ACPdp+qM9 fh6onfNxNnIK2FpDL7WChWMIMtybVUYolYkRng/lWKLX5Tvgp+hGCu9crzEJpWF7 BZLHM7kviw888I7gwQ7gl6sOqIiMSbZ+3gk23AqNpj2Q6Fzk7KaD9H3KVZuOLMSB l0QJeequN6pWYibE6b77k6/ii+XqBGODBqtmN3YS8rrq0q5mIw1FEz76Aqwiw13O /zt/A9+itJOzuMZX2103h/QcBsMYJSzlW0PKb5UF8Q+I7q6MczfDjbI48juwwo6m Q4DwFuszM6njM9qoF2yxC+pbtPTJao8cEH3Y0UEjrWPg0FLk/lBZw+g4A9OWim+g BJ2/s6XmCfOLyjaY1tzpnc+uvs+XNFxhIsSqp25PCMv4EAfzcHjvEfzUZQvQ5E7d KkqnWIDXfLifMUIlS76VVpVUqB+4qQIDAQABAoICAFHz1ErUBKd9K4QHImxD/P9y il/PC3O8uGskFcTSMy0NvBU7ns2YgLLQXv1FztwkYp6P/cxa2m6sE8LN62d9+VwO CHOAUCMLznflfITP0lmq7oYNCzn6QnI3HiLECZZdLcP7ceQlheqI+d1uF0q20TQS o+S1GoHp7cIzH+R/n4ukASC6rMHSwjzGY8EeJeDXGerZWi0yC2+EZFsSui33u/yH v4Vb0LWQyTZ5LtfRzlpiQQp6CWUVv/xvw8yGJ12wbs8VvvDn1sWKr76AqJQU4kfb 1//SbVrdhftcF/+g0Xd6X3VEdOBEbhcVYrD5JmDy5+x/N6EvCdEzMwEVvGbV2z7/ FH9VIn5kK39b9LJPS9AD9MgYbKG6uFVQzRBysYGX8KQo1bJuIDhx+XEefSCOQ+Gx ukfVlCRXdnJy8a/W1jY0Bo6jCFy2lqp4b6fVkBR+3thvFrJXLXo1xV7jgrGOJEvh KZJW61d8jVMEeII43wrOVV0IsKp+bmbQ+eC5Rsz4Y8yBkXOqgrprUVBmU12jgFCa 1GNj5zstJ03VJxTMm2EzLBm7gVb9Fjg0O6xc8rIdF/rLLOV7rTIOcgS76B2TqP4/ v3jCwfbEwdwoWHRAF84XSpjG6P0Lh0GsdhbJwU71EV4zCvIUnd7a2zLp6Zo/3DZS 115vZEfgqyk+Pen5xL3BAoIBAQDwiX9S6lLIUBRIYOT6YfZNG7/4npkKNBY2PqGv t48l0gDcxQd70tGMw47ctUM5LQrJYDEKR3zPPX6cUn1O0UVHnP5JzLzPw8sC463U dzUVXeNKcmHDXWa3fsFGgw+mHC9KMNJCfDqZxNXNP1hndLTdaLD+7FFHva4kqQGy LJJafNT+LPveb5ywkDJ7XmAzLbSlhOGy7jNCp3P86+DEpDLvrw81PYu4WImVvIZ+ 5s8v7PffTVOg1XrmnFD7vF+ovSxnhwT2of26xPjtwbWKTIDg1ZwzCBGut46dlkOX K3HoxXzkk0UZyRv3ejcesX3A5Ib6dSzChrdw9CcC61VY01G1AoIBAQDO5fkgDbWH +sWRtSUolwtCRvGgGa+nRPQcGSoBvHXS0W9S7ruf+h59hE+TBt7B+nXmyNDNSmpa rK/XkBKxSzqaVgrIcWZdxOCS+eP0UgG7jsWwyjWaYAfP1O3Wp0O7/+uZoRQWNFXk CeAQN4AdwZ9Pn1T1Nt1Y+sOycwpD0E8g6ggfPGWNKLq6Oq8DXX5xas3OQXzGd9yu M3nNTvb+i7Aawa/Bdp0YfV0/diBhqjZ/m6704QLGE5bE9bYqYNLxT6u6iVuNZLSM Ibdw0WfV7LLt7ZVxl5I4m/F+crgRmb9Un7wdr8kLI/xXJdbPn8ufL5YDJ+WQeN6F G6g+np3fMTOlAoIBAQCSp+K/lSsAAwM61gkGODBJ9z9mwJwiwntAe5NtZYeb0ZzA /kh/0Jv/LUSvgL0J4VKQUVvVHp0UZjQJ76mDIskQzsGkEXaVXpUqn9LelggBjQsF 2xOMYCg+fMQuz73803Zpz7aC3ueD1aVdzN+DxH55+FjiNQehrB6/L2RfVBmvnijn CFpQ1tA8Ps7otTQGQDnCKXDK/by3SQ3JCbAzdMGxrZSiK3JC5YiNiTKfsO5mFB9V QPpaN48FiA1ATywr35txS7tU/JONCoeTvuWG+vohG1xvKN5PHo+PuYxgYRbEi5SI cNpSzHGGxDdTOXio4S0DC+pMeILkFZiriPyyebV5AoIBAQDMe7ZQ27vCfTKu452q FD5obr14Qmq8owWwj55YwO6iQaQJDzIY1pcz7oTHB0854FSOl4LmotmibHIOVrJi z7tHtipKGOnXWzGpkZiebD6SJHV2WSPJQ4f0/LlkIURslm9AE1dK6sbI7omo/XF9 91OA2jSZdnQl8RFhWRmYFFVgbm1Akey8KrkCPeWjKdBCQBDP/SFY9jYBZZbIN3cd 9OlESJFwX8672YtDoXg3job2b+Pm2kxngAzO9RnpoHBbVyae4gq+H/3hUaF/uzco 0xu008+TyP4XPOjc1HzfyFi1RnohzQ6iGBrZ9ufrpD8XQWy+Cbx1oUArxj3uRc46 POKRAoIBACCXn8r3LYG71D2zLkjVVB93mfcx4abY3r31m+9rXAW6GZVZS+taQLpv zLCqat2G3zBVhXy99ASPy6DcoPFtkYWjdUz9V6jbzv/MtrEtU2kTSzziBPrwU4nl fzzPCuthccNwwEYan9hE33Qx7wJp/Hjk5mllZeBC5A3HhpjWxUsgwoJZpALGQg3L KIcNLj2GJ/8NelNDFr3MhBMMW5wtoXxzktyR/b1aMd54bTYAeluzjbDqBhtyja9r bvD+0sVLbcef7UVK5ruEVakj8QuR6GClkECalGVM1hcpj8LmibxdspA3NuYMliNx SVzCqFfXsK8zOqWYSt/6D5e3BA6cM/8= -----END PRIVATE KEY----- gnome-shell-extension-gsconnect-50/installed-tests/data/vcard-invalid.vcf000066400000000000000000000001661421543444100267650ustar00rootroot00000000000000BEGIN:VCARD VERSION:2.1 N:;Broken;;; FN:Broken T\EL;CELL:555-555-5555 X-KDECONNECT-TIMESTAMP:1598767164977 END:VCARD gnome-shell-extension-gsconnect-50/installed-tests/data/vcard-valid.vcf000066400000000000000000000203151421543444100264340ustar00rootroot00000000000000BEGIN:VCARD VERSION:2.1 N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;=D0=9B=D0=B5=D0=BE=D0=BD=D0=B8=D0=B4=20=4E=69=6B=6F=6C=61=65=76=69=63= =68;;; FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=D0=9B=D0=B5=D0=BE=D0=BD=D0=B8=D0=B4=20=4E=69=6B=6F=6C=61=65=76=69=63= =68 TEL;CELL:778-877-9558 TEL;WORK:778-877-9999 PHOTO;ENCODING=BASE64;JPEG:/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgMCAgMDAwM EAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/ 2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUF BQUFBQUFBQUFBQUFBT/wAARCABgAGADASIAAhEBAxEB/8QAHQAAAgIDAQEBAAAAAAAAAAAABw gFBgIECQMAAf/EADgQAAICAQMDAwIFAgQFBQAAAAECAwQFBhESBxMhAAgiFDEVIzJBUQlhFkJ xgRckM1JiQ5GxweH/xAAaAQACAwEBAAAAAAAAAAAAAAADBAECBQYA/8QALBEAAgIBAwIEBQUB AAAAAAAAAQIAAxEEEiExQRMiUWEjMnGB8BShscHhkf/aAAwDAQACEQMRAD8Apv8AUjQr0PwSt Ses0OpI4gzRbDj9PZYAsBtvuzePiTtuV3B489tL57I6WzNHMYm09PI0plmgmjPlGB8ffx/t+/ roJ/UtMEHSXT1eugjiOcRl52PzP+jOR8PPP4yLvLybc+CQfHrnXWOyE+fSlABrwYppD8PM67+ 2brJiOvmg8dPd+OSqLJRurEGLpI0bBSuw+zI0oT7HdSNjsxKPe+PQ9rTHXHLQlN8bSx8TwMsW yRxuXIRSR8grvx3+/wBv3HoV9FOsec6J60rZ/BSJG42isQSkmOeLkGKOPPjkFYHY7MoI8j05O e1t0e91OFtz5vVh09qWSo9JK151i4RGSOUASFOMhXsgK248BtwN9vSiqdM/TK/xCtXh/EWc+J ZGSBTsOIIIB38f29bWlmpTamxxyVg1abWYxNNxLdtC4DNsPJ2G5/29NTpTol0l0vrH6HVWqor URspXheUExHdpQ0v2AKjguwYj7ONjtsAh1t6NXemGqbiY+OfIaYmkL4zKNGB9VAQXVwASR8QS f423Pp5bVc7YUTKLNYSl0+x4rzNBbfKWu8ANg0aRwcNvHg/Jv9f339bFPN1KXAJdpLYCKFkEg CoWYNvv532UgH7eT9vB9QOb03byGj5859BkokS6ZHrpj/8Al4i6qs8jSoqrH+aqKsRUcVdfP2 LViwtOx2pY1SN2id5ohy4q7SOFC+d/C8CPP+vqDUGhA5HSGulLdbLUqlezV+pm2eJWsJHtD5Y sv7AeDsV5DbcftsGK6X1amGy9ns3cbe1FWctLlZZkFPGkc+avtsvL4sQoB3B2Kbt6QuviFaNC SVbcF2ZgFCk7eRvv/vv+/wDb1ddFaMxeprdXGV6d/I5Sfbt/Sq4BYkfEbAk/cedtvI8/t6Ru0 yEcn9o9VdYeFOJ1g0bqK5mJXtUsti8vjZS0c0dJLDxIwXiQp7QDAHbyWAJ5gL539XgWUx81jZ oGR34v5+6DbjyIO26bnfb7A+RuQPXPjRvsy07l8ZjhkYbdO3YJiIa8FlaQLuWACsvHZg268lI U/MbH1d6Xsvw2nclZNHVGssNepni1uHJIndbieIj2iDN4Qg7fsoIDA+MfFKnhj/z/AGWehjyW EH/9Q/Wsl6tpXA7RLG082QKxhAv6VRWUDym/yUqf+xf4PpMYyQABv6OnvW1KdQddcvWikSSlj I4qcBRQCSEBcuw25tzZlLnyQo9AyNiijdNyT+/rqKRisTEoXbWBMuDu4XiwY/YAb7/7epOhk5 sBZSWo7SoVCyLzZUmQPuVbjswU7fbcN/p6j0klgeQwqyuPDgAEAbj/AOx6wEzF07rMOBAH8qN 9/H8f/vo2IxJq3l6l6q29iyk4gSMRyb8SV7f+bkSST3PBCgeNv4ForZy1+BwWcTVyVqKjGwlm r2XdoAeDfMcmVYx25OKtH9nbdn2HGBMVTKiU495MiqKOEGQLGdAqhRHup47Eu3Eg7koAVBZQd OvjkaN+TU8fJGhIawHY2Ru6sYzsYzsPH3HlRxO+/qpAnozfSP3jUsBm7lDUlaLIafsUTUNi1R inlR3lLM5IQNsAxcBQAZFUlVDHiYtWdLukPu307euaRkj0xq6jNWS3k6FZfoJmBkWWSRVReKF 5EImk4cg2/gIfSHaYvUat6GpejavWmgJuNJJKyWtmEyRskRB7bGONeO+/JixYbLwtvSPVU3TL VVzLUrVjF5nHDmpqX2qyKQdmKAAiQIN90LbsCT52JVR6ACXr4M9Njrf7f9Ve3rV9fTWp44u5b jEla3Ap7cqj78WIG/6h5G4O42J9Q3TXqLmtFZBjp3YXbhMbSzRKxj/8kZiOLjcnluCPHn9/To ZCjT9z2L0TWmowZW7piO4Hp5ecVVeEpxanyiPJGcBZU8P2CNtyrbJUcx7S8brHTM1jCV7ukc0 yx2JTkZlv42Vzz7ywz1ucqs4FccWibZ+QBVfsAalNu23rCqrH5Zcfbt1N0hHh8eueuY4an5Rs 8sc8CmTuoeZ5s4O/BoUMfxMZBH/d6a/AfR5ejG8L7xKitQtK7sktcONuZDAHxt4Pkeft4Prnt hermgOgF7HYb/hpdq536TlayWWpxWZ5Jn2IlhAm48CeS7IyqQoBBI8GXEf1DumcVIRWsHqKrZ fj8qtCGOMkA7vwE54A7g8U8Hc77bA+s63TsTlF4lxk9Zz/AOompZNba8z+bmURtfuy2OAO4QM xIUf+IHgf2A9QZZ40Pb2AI/VsP/n9vWcPkF9tt9x5G/rxmkHlVAH8kePXRgY4EWAwJ5HcEEHz 99x6zglIBjI3QkbgICT/AKH9v39fNtx/MdgSPtxB8f8Av6k8Hi7OcuR1MVGJbZUkI8qRs5+54 ksOR8eFG5/j7+ryZK46OvClXKxW3xJpyoz850lsJ/0/zIIiqbnkZHGzeAACVIDP5Sa6uUL1s4 mb6atK0ScEThyjiZHRd9ywXnFG+3IndVLMxHL1r2MVcuZC7Hk3kgvI5eaXIzdtju4UkhxzZuR 3IHy23JGwJHtd01SqU7k6SWJ/pLSQyPGI5YhuJN1JDg/qRdmA2YFj8SArV4k4muVn1PkZr8ta rEJVKrHUENVEKqqjZBsCBupP7n5Mx/U3ow19E9KNP5m3S1VqXKdyJIZXq1VDyxbyMXQShCsjc HQjiyBvJdYySsUFj8XJpmvDG0KYeZJwkjGZY7NaTfirmNmMyMGSFy68eJPHiFbdrJp6lj792v Xu4SrnM3DHM9iKRF5QBZ/ywrMzGzI4fiqMFLO8AJA5CRO2zPGcD2jKVsOcZjiw+3Ppbovplpr UNLqxJgaGo2it0KEdR7K2zx/LWRowJOXEyIAzBQ0nnmRu4F6ge9OWlqK9BhMjHmsNTm4VLleN ojbB8nksgDKB8uOw+Pjz5BDFah01hs31o6b4XIVqsmO6U4S5mc/WoVBLTETpXNaIDjsVaInz+ 7K+xAAIUDF4XQOV68/iOGd8bp+bUkUcVTFMSopvIp7DRNuxYHkpkjLR/HcBQV3ylrrcbrcnAz DU2PW3k4zL50zzV/rhnsTqTM9O7ORw0VpsR+OzAWEEz8ZI90YfF17bnmS48gbLuxLsaf6H43A 2FtVOEVeXaREaQpIiqu/bO7cd1Ztxv5G36iD4ndbZiqPZxisxR0jXx9i3laVmviYeSOWkshop H2VXWXtujsCAUffyeIY2el9VlMRXn+ljrPKFeepOpV4HIHMkEjY+NzuP5B8/ZK1/EwAMD0lgW Qs+e84LROIof5/n14PKvMMBuP4bz6lNOacy+tMrVw+Dx0+TyNhwkcFdOTEk7D/QefufHpi9C+ z2PFrQyevbNm9BakSJcTpzYurEblJLUvCBZNioSNHbm0kYDDmvLqntSv5jM2L1o/Qmc17farh qTWmjXnLKzrHFEu4BZ3YhVHkfc+inlfarrjS1CtkKmUwN/wCpTh2qOSUuqvFMx58gqqvCGf5k 8dlJ32IPop29Q29EYO3+G6UsaT0jEk0uO+qqwwwZGUbvEZDZdHnZQs0bBHdh8lC8gAKBJrrK5 HWskdsJms5Y4y0rctr6hLBDCQM03z3biZI24bL85Pmp7pdQ3WOfJgQyJuPMF1fKX8vdhrSxV7 F2LaruYY5RKe5zHNtt3A4n5Et4A+XHYerdncNJpvFZ7E5vD1hZsbO+QnslJwyOWWRVTdVRlRR sV3dmbbkwTtlPod7W01rkcdls9kIlxFjJw0hcr3IkrtGGhZ27zeCeHc4/lnk/bB5FuJvXU3or pbS+k85no85DXt4mpHXxAuYmnwvxh6kU5aJl5SFe7KvDiWR4nAHBV9AbWVCzYDNAaOzZuYRaN KQx5Cpp2RZqEFiW6yRQdkkVAApWR2EZZgXIXyW3AKn7fF2NKx1tAdDs91Flw1cwrDFKtSKQo0 rQlQOR7iOUZJADy5luR5L5IKiaOOKqZD6SvBLD9VDWlSd4OUomdlMaFDsBHzBbY/ELwcmRhHs xPuL12MD7ZPwz6dEuWJlw0RDizw3dLBIfltGwCyq23I7lAdipIS1TGy5Kx3P7TTor8PTO57SF 1/1am6Y9I9Y49ssMn1P6jzV57+Qx1nl2aqL+VGZIwQ3GP4+CrM7cjyCuGIv9ProQMrgbU2o8T G8WXtSWvw+7TBiuQQ1YCiEnY7Mb/IFRsO3KpLcvAQ9tXthyfUC7PDmqrQ2LFWCOtNFISKyPKy g/lqwLskE/kkBUJJJJ29dPtSW6XR/SlTSekVpyaokmWpLe7agQmaZVffbcKQ7xAAAk7jf7MwB qLlRDWhzzyf6ETWktYvlwT2/syp+71dRZTAYHp5pLUl3CZa1Klyxl6VbnJYmXcMg2KqpLBSQC B8uO3nZliy3SzW3TPp9k8rr3rprOrDSZRUSGaSHfjuxQCRydiOI+y7E7kFRy9WzSeS1xIgetb is5aoIac09hSOx3/lHLGPkihiZmaUiX9gCPl6CnvO1/m+qGTxGgrN8qFyEdWKZYyIpW5bHusD 5Kcwx+Ow2Y7DcbpUWPa4TOB9BNazTfp0BC5Pv3l0/xrf0LpKlhNK6JbECAi3RwjaagyE6SryJ j7jNIJCWR1M4RdiGAVnRwu1kZNVy/R28dJ9T1DxkdirRq2pyatBZ2VPqyWbixEdow/KOLYunc jMTJHCbLdChTyc1CtJaOTzhqG/euMJZI6qiIOsp57hn2X+WUSDZkViAunuO69/4L1dpbH4Rak 2Ulyt2xdx6RKYp60kzwxpIm0ZYhUYoA6/PYg7cSdZFLHAE4gWbukuuo+lFLWesdc4Ez6loZ2h Q+jheKUpcyAs23rhJ4Tv208bxmEiIxpEG4RxlCvlPo7puDVWqclo6nkrWP05JZoJPMEMomhjY SMGjIWduzDLY4gKGA3B3JVmJ92PuuwHSr3DVs3jqf+Jja/DZcnYLiSBoYZTPFBCOQ4PGHSZHI 8sxHgluC2dI+rdw9KrPcsQ5bNYzqBV17Nbsv9NVgPb4Sy2ZBtwDMyoI0HJmbZNyADcV2lCyng x+m7YckZhx9u0uH1xPqSnk2gwumcUi5SedLkcctSOEicSwcWBAlCHtEAKqksCR22YT9ROq1Lq XpiXL0bUFivhsnPmcoliGYwWGsNHNEj9oBGnSRrkMccr8O3XkZWPMs2z7eNaVchmLmrtQ2alG TLGe7XrSU2kjNwyAyvHD3u5KZkjsQK7A855Y0U/lPwmunWs+mPUPHY3Gt09a5Xp30NbJ1GirZ E85LpTurGVjAijSNyqltizuPIChU1rpmdmUkcfnP57TbFturKgNziDHp5o6qlqvmoK8VqTu1Z kmtJOixVtomRgocq/EyiPj5XjGgG+5PrZ0/hs3r33SaF0DqCOJ6v1aWIBYPMTB64kSVzvuxPE NsSfJIblsd3Fy3TzS69MLWf0nFTqYulHJJLTrWn5TSGNGCLIvAAg/TcnIOwUhvsNlT6TW6Q93 vT3JakjjkpYuGw80SP3CZkksKgVV/zd10bgB4JYfsdhae83tZYewIHqOI3qlWmpK09QT6HmOn h+qGDxGoNVZqCpZrWtA6Jikr1a78ZVs2IpTOxUht5R2PDMpPzO6+W7ikam95OttN5fFZeGN8n hslYbNpFkInqs0kUzxKrDkQrNHBDIQu4YuPG7bm5+7zrND0Z1Jm6ml4FgvZq5GuSjj/AOWeei iQFFaSIq5Rz3YX4sNwG2ZSAQrWnup1zRODjGmLtj8CsW5iFnbnNRlkWCRI37fEc+ULqsnFVkA YgIQyxmo0m+neVz6An87xVNSiXnLY9+sL9z3f4JbN2CKpPQaGR4Y7TEys4j+MUqqq8Qe2FQBu R2LDmo9CfRXU85fqxjctYefIVsa5ajSmUlubNwBUKWJP6fG7MWbxy9afWfqXk+p+oMBksnfkg S/GkksUUK1xyJG8yqoC8OYZf1EkwFiV3UBioun+kOmGi8frSapV/FOwTjxciLRV0WP/AKjcW5 u3xCId1I8vyYBEa3hU6ZB5TubjrmOeNbqrCA42oQemPf1jeanwF2KSO5Q+e8UcadgkrxCIG+P 3Ibtjff8AuPSSax9nee1RrR1p2rMEsM8TcewWGx38K3LiiqebHz4LPsG229SWjf6i1NpK/wDi vSctOTsstizp9ldJ3LbgiCUrx+5/9Q/q/t6MGhffX0tvWIXu5qXT6xygAW8bIjSqBsHIgWRdz 9gD+kAHz9vR/j1HO0/zOJSrb0MWnI+zPU+pNZNiMnqejwx1QU47FWFJJHdQTHGIhIrFNuQBG5 UKqhT4ACfU3oNqzpbrytpLI1JHvXlRqR4mJbaMxVGTnt4ZhsN/7fY7gOv1h6udIcVJY1b091/ XxnUmRG+mmxcLSV91Ys0csksY+JeJSNwf8rADcM37H72unOt8ng8trzT2HoauxFR61fUlBBe7 a83YqIHXhIHX4jkFIMjEOhBPqV1eoTnYSPpzNFNNvGdwERbU/SrWWlXuY67jbTw0rslaaOue6 sdhV+QZUJ4txG/kDdRuPHol+2mLIYHUc9DIfU0IbBfi8hRVrWYg/KOaOQqRzVHXwRy4MuzcW4 NzlPfX0R0ZfyOextHJav1XeQKJnow04o9y4PdKqFkfaGtyfgSwSPbwpBV/SfuZ0viL9i/a00Z rH1fdWPlurgksdh9h8izb7A+QPO3qbLr9RSVNJ5jOkSuq3c1gGI9HSz6zE9LsJjp2lkFG3DGI pHLO8W8iWIgdwCBx+O+2+yIA23yV/qTS0j0izZydySHO5KWlYyGPxcTho8bObMXdlAU8hyEUo J8kGJgx/XtEaz97ebylmBcLjxHchhMS2JYEPbRn3TY7NxALclceSCNv1eRbntRSaMy1x9RaeG UmvxkccsRJGIOTHsKYm2VSV5owA2c+VYDl6S0mjettz8Z7RrWagWjanQd5RdYZX/E31mWdZhS szrair1p+9DQWR5TJEFZnaMK3xQOylgqtseQ2Inte6V47Xt+X8StnHrGqzo00YKzETKoHIbMI jH9UHPMA8f3KAHDI6St4r2/X6+QhEeWsWmviKRgstTiwMkMobiqs8YSX95T2VXiq77y/Qi5LY 6XZm5hsqK+oMQVlaouL7s9gBw0SpOHBA5iPYbf5W8qB8tS+0+A3hnHOIDSUgahRYM8Zmt7v9H 09N67uZevap8rV11hSnD2pZIxEilmZD224ujAsqqXcyMSdwRt6U11kvcJoyXRt+s0l6pDBDDY rRP8AoQBELBWPNhwHhU+W7sd2Zm9VTrD1SPUjTtWG3jjNqGq5WeTuOixR7cyY4dzvuSN2Y+Ao AUDyfLoLrSHpJ1hxzXsZ9fUyFcULlazAqOolUD4hhtuCR4I+4/cgH1Va2OmG8eden2hrbEXVN sPkbr9/9n//2Q== X-KDECONNECT-TIMESTAMP:1598767164977 END:VCARD gnome-shell-extension-gsconnect-50/installed-tests/fixtures/000077500000000000000000000000001421543444100244775ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/installed-tests/fixtures/backend.js000066400000000000000000000431551421543444100264340ustar00rootroot00000000000000'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Config = imports.config; const Core = imports.service.core; /** * TCP Port Constants */ const DEFAULT_PORT = 2716; const TRANSFER_MIN = 2739; const TRANSFER_MAX = 2764; /** * A simple IP-based backend for tests. This should ostensibly be kept up to * date with backends/lan.js as it is essentially a clone without the TLS parts. */ var ChannelService = GObject.registerClass({ GTypeName: 'GSConnectMockChannelService', Properties: { 'port': GObject.ParamSpec.uint( 'port', 'Port', 'The port used by the service', GObject.ParamFlags.READWRITE, 0, GLib.MAXUINT16, DEFAULT_PORT ), }, }, class MockChannelService extends Core.ChannelService { _init(params = {}) { super._init(params); // this._tcp = null; this._udp4 = null; this._udp6 = null; } get channels() { if (this._channels === undefined) this._channels = new Map(); return this._channels; } get name() { return 'Mock Backend'; } get port() { if (this._port === undefined) this._port = DEFAULT_PORT; return this._port; } set port(port) { if (this.port === port) return; this._port = port; } _initTcpListener() { this._tcp = new Gio.SocketService(); // NOTE: we brute-force an open port so tests can run concurrently while (true) { try { this._tcp.add_inet_port(this.port, null); break; } catch (e) { this._port++; } } this._tcp.connect('incoming', this._onIncomingChannel.bind(this)); } async _onIncomingChannel(listener, connection) { try { const host = connection.get_remote_address().address.to_string(); // Create a channel const channel = new Channel({ backend: this, host: host, port: this.port, }); // Accept the connection await channel.accept(connection); channel.identity.body.tcpHost = channel.host; channel.identity.body.tcpPort = this.port; this.channel(channel); } catch (e) { logError(e); } } _initUdpListener() { // Default broadcast address this._udp_address = Gio.InetSocketAddress.new_from_string( '255.255.255.255', this.port ); try { this._udp6 = Gio.Socket.new( Gio.SocketFamily.IPV6, Gio.SocketType.DATAGRAM, Gio.SocketProtocol.UDP ); this._udp6.set_broadcast(true); // Bind the socket const inetAddr = Gio.InetAddress.new_any(Gio.SocketFamily.IPV6); const sockAddr = Gio.InetSocketAddress.new(inetAddr, this.port); this._udp6.bind(sockAddr, false); // Input stream this._udp6_stream = new Gio.DataInputStream({ base_stream: new Gio.UnixInputStream({ fd: this._udp6.fd, close_fd: false, }), }); // Watch socket for incoming packets this._udp6_source = this._udp6.create_source(GLib.IOCondition.IN, null); this._udp6_source.set_callback(this._onIncomingIdentity.bind(this, this._udp6)); this._udp6_source.attach(null); } catch (e) { this._udp6 = null; } // Our IPv6 socket also supports IPv4; we're all done if (this._udp6 && this._udp6.speaks_ipv4()) { this._udp4 = null; return; } try { this._udp4 = Gio.Socket.new( Gio.SocketFamily.IPV4, Gio.SocketType.DATAGRAM, Gio.SocketProtocol.UDP ); this._udp4.set_broadcast(true); // Bind the socket const inetAddr = Gio.InetAddress.new_any(Gio.SocketFamily.IPV4); const sockAddr = Gio.InetSocketAddress.new(inetAddr, this.port); this._udp4.bind(sockAddr, false); // Input stream this._udp4_stream = new Gio.DataInputStream({ base_stream: new Gio.UnixInputStream({ fd: this._udp4.fd, close_fd: false, }), }); // Watch input socket for incoming packets this._udp4_source = this._udp4.create_source(GLib.IOCondition.IN, null); this._udp4_source.set_callback(this._onIncomingIdentity.bind(this, this._udp4)); this._udp4_source.attach(null); } catch (e) { this._udp4 = null; // We failed to get either an IPv4 or IPv6 socket to bind if (this._udp6 === null) throw e; } } _onIncomingIdentity(socket) { let host, data, packet; // Try to peek the remote address try { host = socket.receive_message( [], Gio.SocketMsgFlags.PEEK, null )[1].address.to_string(); } catch (e) { logError(e); } // Whether or not we peeked the address, we need to read the packet try { if (socket === this._udp6) data = this._udp6_stream.read_line_utf8(null)[0]; else data = this._udp4_stream.read_line_utf8(null)[0]; // Discard the packet if we failed to peek the address if (host === undefined) return; packet = new Core.Packet(data); packet.body.tcpHost = host; this._onIdentity(packet); } catch (e) { logError(e); } return GLib.SOURCE_CONTINUE; } async _onIdentity(packet) { try { // Bail if the deviceId is missing if (!packet.body.hasOwnProperty('deviceId')) return; // Silently ignore our own broadcasts if (packet.body.deviceId === this.identity.body.deviceId) return; // Create a new channel const channel = new Channel({ backend: this, host: packet.body.tcpHost, port: packet.body.tcpPort, identity: packet, }); // Check if channel is already open with this address if (this.channels.has(channel.address)) return; this._channels.set(channel.address, channel); // Open a TCP connection const connection = await new Promise((resolve, reject) => { const address = Gio.InetSocketAddress.new_from_string( packet.body.tcpHost, packet.body.tcpPort ); const client = new Gio.SocketClient({enable_proxy: false}); client.connect_async(address, null, (client, res) => { try { resolve(client.connect_finish(res)); } catch (e) { reject(e); } }); }); // Connect the channel and attach it to the device on success await channel.open(connection); this.channel(channel); } catch (e) { logError(e); } } broadcast(address = null) { try { // Try to parse strings as : if (typeof address === 'string') { const [host, portstr] = address.split(':'); const port = parseInt(portstr) || this.port; address = Gio.InetSocketAddress.new_from_string(host, port); } // Broadcast to the network if no address is specified if (!(address instanceof Gio.InetSocketAddress)) address = this._udp_address; // Broadcast on each open socket if (this._udp6 !== null) this._udp6.send_to(address, this.identity.serialize(), null); if (this._udp4 !== null) this._udp4.send_to(address, this.identity.serialize(), null); } catch (e) { logError(e, address); } } buildIdentity() { this._identity = new Core.Packet({ id: 0, type: 'kdeconnect.identity', body: { deviceId: this.id, deviceName: this.name, deviceType: 'desktop', protocolVersion: 7, incomingCapabilities: [], outgoingCapabilities: [], tcpPort: this.port, }, }); } start() { if (this.active) return; // Start TCP/UDP listeners if (this._tcp === null) this._initTcpListener(); if (this._udp4 === null && this._udp6 === null) this._initUdpListener(); this._active = true; this.notify('active'); } stop() { if (this._tcp !== null) { this._tcp.stop(); this._tcp.close(); this._tcp = null; } if (this._udp6 !== null) { this._udp6_source.destroy(); this._udp6_stream.close(null); this._udp6.close(); this._udp6 = null; } if (this._udp4 !== null) { this._udp4_source.destroy(); this._udp4_stream.close(null); this._udp4.close(); this._udp4 = null; } for (const channel of this.channels.values()) channel.close(); this._active = false; this.notify('active'); } destroy() { try { this.stop(); } catch (e) { logError(e); } } }); /** * A simple IP-based channel for tests */ var Channel = GObject.registerClass({ GTypeName: 'GSConnectMockChannel', }, class MockChannel extends Core.Channel { _init(params) { super._init(); Object.assign(this, params); } get address() { return `mock://${this.host}:${this.port}`; } get host() { if (this._host === undefined) this._host = null; return this._host; } set host(host) { this._host = host; } get port() { if (this._port === undefined) { if (this.identity && this.identity.body.tcpPort) this._port = this.identity.body.tcpPort; else return DEFAULT_PORT; } return this._port; } set port(port) { this._port = port; } _receiveIdent(connection) { return new Promise((resolve, reject) => { this.input_stream.read_line_async( GLib.PRIORITY_DEFAULT, this.cancellable, (stream, res) => { try { const data = stream.read_line_finish_utf8(res)[0]; this.identity = new Core.Packet(data); if (!this.identity.body.deviceId) throw new Error('missing deviceId'); resolve(); } catch (e) { reject(e); } } ); }); } _sendIdent(connection) { return new Promise((resolve, reject) => { connection.get_output_stream().write_all_async( this.backend.identity.serialize(), GLib.PRIORITY_DEFAULT, this.cancellable, (stream, res) => { try { resolve(stream.write_all_finish(res)); } catch (e) { reject(e); } } ); }); } async accept(connection) { try { this._connection = connection; this.backend.channels.set(this.address, this); this.input_stream = new Gio.DataInputStream({ base_stream: this._connection.get_input_stream(), }); await this._receiveIdent(this._connection); } catch (e) { this.close(); return e; } } async open(connection) { try { this._connection = connection; this.backend.channels.set(this.address, this); this.input_stream = new Gio.DataInputStream({ base_stream: this._connection.get_input_stream(), }); await this._sendIdent(this._connection); } catch (e) { this.close(); return e; } } close() { if (this.closed) return; this._closed = true; this.notify('closed'); this.backend.channels.delete(this.address); this.cancellable.cancel(); if (this._connection) this._connection.close_async(GLib.PRIORITY_DEFAULT, null, null); if (this.input_stream) this.input_stream.close_async(GLib.PRIORITY_DEFAULT, null, null); if (this.output_stream) this.output_stream.close_async(GLib.PRIORITY_DEFAULT, null, null); } async download(packet, target, cancellable = null) { const connection = await new Promise((resolve, reject) => { const client = new Gio.SocketClient({enable_proxy: false}); const address = Gio.InetSocketAddress.new_from_string( this.host, packet.payloadTransferInfo.port ); client.connect_async(address, cancellable, (client, res) => { try { resolve(client.connect_finish(res)); } catch (e) { reject(e); } }); }); const source = connection.get_input_stream(); // Start the transfer const transferredSize = await this._transfer(source, target, cancellable); if (transferredSize !== packet.payloadSize) { throw new Gio.IOErrorEnum({ code: Gio.IOErrorEnum.PARTIAL_INPUT, message: 'Transfer incomplete', }); } } async upload(packet, source, size, cancellable = null) { // Start listening on the first available port between 1739-1764 const listener = new Gio.SocketListener(); let port = TRANSFER_MIN; while (port <= TRANSFER_MAX) { try { listener.add_inet_port(port, null); break; } catch (e) { if (port < TRANSFER_MAX) { port++; continue; } else { throw e; } } } // Listen for the incoming connection const acceptConnection = new Promise((resolve, reject) => { listener.accept_async( cancellable, (listener, res, source_object) => { try { resolve(listener.accept_finish(res)[0]); } catch (e) { reject(e); } } ); }); // Notify the device we're ready packet.body.payloadHash = this.checksum; packet.payloadSize = size; packet.payloadTransferInfo = {port: port}; this.sendPacket(new Core.Packet(packet)); // Accept the connection and configure the channel const connection = await acceptConnection; const target = connection.get_output_stream(); // Start the transfer const transferredSize = await this._transfer(source, target, cancellable); if (transferredSize !== size) { throw new Gio.IOErrorEnum({ code: Gio.IOErrorEnum.PARTIAL_INPUT, message: 'Transfer incomplete', }); } } _transfer(source, target, cancellable) { return new Promise((resolve, reject) => { target.splice_async( source, (Gio.OutputStreamSpliceFlags.CLOSE_SOURCE | Gio.OutputStreamSpliceFlags.CLOSE_TARGET), GLib.PRIORITY_DEFAULT, cancellable, (target, res) => { try { resolve(target.splice_finish(res)); } catch (e) { reject(e); } } ); }); } async rejectTransfer(packet) { try { if (!packet || !packet.hasPayload()) return; if (packet.payloadTransferInfo.port === undefined) return; const connection = await new Promise((resolve, reject) => { const client = new Gio.SocketClient({enable_proxy: false}); const address = Gio.InetSocketAddress.new_from_string( this.host, packet.payloadTransferInfo.port ); client.connect_async(address, null, (client, res) => { try { resolve(client.connect_finish(res)); } catch (e) { resolve(); } }); }); connection.close_async(GLib.PRIORITY_DEFAULT, null, null); } catch (e) { logError(e, this.address); } } }); gnome-shell-extension-gsconnect-50/installed-tests/fixtures/components/000077500000000000000000000000001421543444100266645ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/installed-tests/fixtures/components/clipboard.js000066400000000000000000000012541421543444100311630ustar00rootroot00000000000000'use strict'; const {GObject} = imports.gi; var Component = GObject.registerClass({ GTypeName: 'MockClipboard', Properties: { 'text': GObject.ParamSpec.string( 'text', 'Text Content', 'The current text content of the clipboard', GObject.ParamFlags.READWRITE, '' ), }, }, class MockClipboard extends GObject.Object { get text() { if (this._text === undefined) this._text = 'initial'; return this._text; } set text(content) { if (this.text === content) return; this._text = content; this.notify('text'); } }); gnome-shell-extension-gsconnect-50/installed-tests/fixtures/components/input.js000066400000000000000000000003211421543444100303550ustar00rootroot00000000000000'use strict'; var Component = class { clickPointer() {} doubleclickPointer() {} pressPointer() {} releasePointer() {} movePointer() {} scrollPointer() {} pressKeys() {} }; gnome-shell-extension-gsconnect-50/installed-tests/fixtures/components/mpris.js000066400000000000000000000100471421543444100303560ustar00rootroot00000000000000'use strict'; const {GObject} = imports.gi; const MPRIS = imports.service.components.mpris; const MockMediaPlayer = GObject.registerClass({ GTypeName: 'MockMediaPlayer', }, class MockMediaPlayer extends MPRIS.Player { _init(identity) { super._init(); this._Identity = identity; this._Position = 0; } /** * Update the player with an object of properties and values. * * @param {Object} obj - A dictionary of properties */ update(obj) { for (const [propertyName, propertyValue] of Object.entries(obj)) this[`_${propertyName}`] = propertyValue; // An arbitrary property to notify this.notify('Volume'); } Next() { if (!this.CanGoNext) throw new GObject.NotImplementedError(); this.Metadata['xesam:title'] = 'Track 2'; this.notify('Metadata'); } Previous() { if (!this.CanGoPrevious) throw new GObject.NotImplementedError(); this.Metadata['xesam:title'] = 'Track 1'; this.notify('Metadata'); } Pause() { if (!this.CanPause) throw new GObject.NotImplementedError(); this._PlaybackStatus = 'Paused'; this.notify('PlaybackStatus'); } PlayPause() { if (!this.CanPlay && !this.CanPause) throw new GObject.NotImplementedError(); if (this.PlaybackStatus === 'Playing') this._PlaybackStatus = 'Paused'; else this._PlaybackStatus = 'Playing'; this.notify('PlaybackStatus'); } Stop() { this._PlaybackStatus = 'Stopped'; this.notify('PlaybackStatus'); } Play() { if (!this.CanPlay) throw new GObject.NotImplementedError(); this._PlaybackStatus = 'Playing'; this.notify('PlaybackStatus'); } Seek(offset) { if (!this.CanSeek) throw new GObject.NotImplementedError(); this._Position += offset; this.emit('Seeked', offset); } SetPosition(trackId, position) { const offset = this._Position - position; this.Seek(offset); } }); var Component = GObject.registerClass({ GTypeName: 'MockMPRISManager', Signals: { 'player-added': { param_types: [GObject.TYPE_OBJECT], }, 'player-removed': { param_types: [GObject.TYPE_OBJECT], }, 'player-changed': { param_types: [GObject.TYPE_OBJECT], }, 'player-seeked': { param_types: [GObject.TYPE_OBJECT, GObject.TYPE_INT64], }, }, }, class MockMPRISManager extends GObject.Object { _init() { super._init(); this._players = new Map(); this._paused = new Map(); } addPlayer(identity) { const player = new MockMediaPlayer(identity); player.connect('notify', () => this.emit('player-changed', player)); player.connect('Seeked', this.emit.bind(this, 'player-seeked')); this._players.set(identity, player); this.emit('player-added', player); return player; } removePlayer(identity) { const player = this._players.get(identity); if (player === undefined) return; this._players.delete(identity); this.emit('player-removed', player); player.run_dispose(); } getPlayer(identity) { for (const player of this._players.values()) { if (player.Identity === identity) return player; } return null; } hasPlayer(identity) { for (const player of this._players.values()) { if (player.Identity === identity) return true; } return false; } getIdentities() { const identities = []; for (const player of this._players.values()) { const identity = player.Identity; if (identity) identities.push(identity); } return identities; } pauseAll() { } unpauseAll() { } }); gnome-shell-extension-gsconnect-50/installed-tests/fixtures/components/notification.js000066400000000000000000000007721421543444100317160ustar00rootroot00000000000000'use strict'; const {Gio, GLib, GObject} = imports.gi; var Component = GObject.registerClass({ GTypeName: 'GSConnectMockNotificationListener', Signals: { 'notification-added': { flags: GObject.SignalFlags.RUN_LAST, param_types: [GLib.Variant.$gtype], }, }, }, class MockListener extends GObject.Object { fakeNotification(notif) { const variant = GLib.Variant.full_pack(notif); this.emit('notification-added', variant); } }); gnome-shell-extension-gsconnect-50/installed-tests/fixtures/components/pulseaudio.js000066400000000000000000000113651421543444100314020ustar00rootroot00000000000000'use strict'; const Tweener = imports.tweener.tweener; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; class MockStream { constructor(mixer, id) { this._mixer = mixer; this._id = id; this._max = this._mixer.get_vol_max_norm(); } get display_name() { return `Stream ${this.id}`; } get id() { if (this._id === undefined) this._id = Math.floor(Math.random() * 100); return this._id; } get is_muted() { return this.muted; } get muted() { if (this._muted === undefined) this._muted = false; return this._muted; } set muted(muted) { if (this.muted === muted) return; this._muted = muted; } get name() { return `${this.id}`; } // Volume is a double in the range 0-1 get volume() { if (this._volume === undefined) this._volume = 1.0; return this._volume; } set volume(volume) { if (this.volume === volume) return; this._volume = volume; } change_is_muted(muted) { this.muted = muted; } fade(value, duration = 1) { Tweener.removeTweens(this); if (this.volume > value) { this._mixer.fading = true; Tweener.addTween(this, { volume: value, time: duration, transition: 'easeOutCubic', onComplete: () => { this._mixer.fading = false; }, }); } else if (this.volume < value) { this._mixer.fading = true; Tweener.addTween(this, { volume: value, time: duration, transition: 'easeInCubic', onComplete: () => { this._mixer.fading = false; }, }); } } } var Component = GObject.registerClass({ GTypeName: 'GSConnectMockMixer', Signals: { 'output-added': { param_types: [GObject.TYPE_UINT], }, 'output-removed': { param_types: [GObject.TYPE_UINT], }, 'stream-changed': { param_types: [GObject.TYPE_UINT], }, }, }, class MockMixer extends GObject.Object { _init() { super._init(); this._sinks = new Map([ [0, new MockStream(this, 0)], ]); this._sources = new Map([ [0, new MockStream(this, 0)], ]); this._previousVolume = undefined; this._volumeMuted = false; this._microphoneMuted = false; } get fading() { if (this._fading === undefined) this._fading = false; return this._fading; } set fading(bool) { if (this.fading === bool) return; this._fading = bool; if (this.fading) this.emit('stream-changed', this.output.id); } get input() { if (this._input === undefined) this._input = this._sources.get(0); return this._input; } get output() { if (this._output === undefined) this._output = this._sinks.get(0); return this._output; } get_sinks() { return Array.from(this._sinks.values()); } get_vol_max_norm() { return 65536; } lookup_sink(id) { const sink = this._sinks.get(id); return sink || null; } lowerVolume(duration = 1) { try { if (this.output.volume > 0.15) { this._previousVolume = Number(this.output.volume); this.output.fade(0.15, duration); } } catch (e) { logError(e); } } muteVolume() { try { if (this.output.muted) return; this.output.muted = true; this._volumeMuted = true; } catch (e) { logError(e); } } muteMicrophone() { try { if (this.input.muted) return; this.input.muted = true; this._microphoneMuted = true; } catch (e) { logError(e); } } restore() { try { if (this._microphoneMuted) { this.input.muted = false; this._microphoneMuted = false; } if (this._volumeMuted) { this.output.muted = false; this._volumeMuted = false; } if (this._previousVolume !== undefined) { this.output.fade(this._previousVolume); this._previousVolume = undefined; } } catch (e) { logError(e); } } }); gnome-shell-extension-gsconnect-50/installed-tests/fixtures/components/session.js000066400000000000000000000013201421543444100307010ustar00rootroot00000000000000'use strict'; var Component = class MockSession { get idle() { if (this._idle === undefined) this._idle = false; return this._idle; } get locked() { if (this._locked === undefined) this._locked = false; return this._locked; } get active() { // Active if not idle and not locked return !(this.idle || this.locked); } /** * Update the session with an object of properties and values. * * @param {Object} obj - A dictionary of properties */ update(obj) { for (const [propertyName, propertyValue] of Object.entries(obj)) this[`_${propertyName}`] = propertyValue; } }; gnome-shell-extension-gsconnect-50/installed-tests/fixtures/components/sound.js000066400000000000000000000032401421543444100303510ustar00rootroot00000000000000'use strict'; const {Gio, GLib} = imports.gi; var Component = class MockPlayer { constructor() { this._playing = new Set(); } async playSound(name, cancellable) { try { if (!(cancellable instanceof Gio.Cancellable)) cancellable = new Gio.Cancellable(); this._playing.add(cancellable); await new Promise((resolve, reject) => { GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => { if (cancellable.is_cancelled()) { const error = new Gio.IOErrorEnum({ code: Gio.IOErrorEnum.CANCELLED, message: 'Operation Cancelled', }); reject(error); } else { resolve(); } return GLib.SOURCE_REMOVE; }); }); } catch (e) { if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) logError(e); } finally { this._playing.delete(cancellable); } } async loopSound(name, cancellable) { try { if (!(cancellable instanceof Gio.Cancellable)) cancellable = new Gio.Cancellable(); this._playing.add(cancellable); while (!cancellable.is_cancelled()) await this.playSound(name, cancellable); } catch (e) { if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) logError(e); } finally { this._playing.delete(cancellable); } } }; gnome-shell-extension-gsconnect-50/installed-tests/fixtures/components/upower.js000066400000000000000000000017311421543444100305450ustar00rootroot00000000000000'use strict'; const {GObject} = imports.gi; var Component = GObject.registerClass({ GTypeName: 'GSConnectMockBattery', Signals: { 'changed': {flags: GObject.SignalFlags.RUN_FIRST}, }, }, class MockBattery extends GObject.Object { get charging() { if (this._charging === undefined) this._charging = false; return this._charging; } get is_present() { if (this._is_present === undefined) this._is_present = true; return this._is_present; } get level() { if (this._level === undefined) this._level = -1; return this._level; } get threshold() { if (this._threshold === undefined) this._threshold = 0; return this._threshold; } update(obj) { for (const [propertyName, propertyValue] of Object.entries(obj)) this[`_${propertyName}`] = propertyValue; this.emit('changed'); } }); gnome-shell-extension-gsconnect-50/installed-tests/fixtures/mpris.js000066400000000000000000000055321421543444100261740ustar00rootroot00000000000000'use strict'; const {Gio, GLib, GObject} = imports.gi; const Config = imports.config; const DBus = imports.service.utils.dbus; const MPRIS = imports.service.components.mpris; /* * A class for mirroring a remote Media Player on DBus */ const MPRISIface = Config.DBUS.lookup_interface('org.mpris.MediaPlayer2'); const MPRISPlayerIface = Config.DBUS.lookup_interface('org.mpris.MediaPlayer2.Player'); var MockPlayer = GObject.registerClass({ GTypeName: 'GSConnectMockPlayer', }, class MockPlayer extends MPRIS.Player { _init(identity) { super._init(); this._Identity = identity; this._ownerId = 0; this._connection = null; this._applicationIface = null; this._playerIface = null; } async export() { if (this._connection === null) { this._connection = await DBus.newConnection(); this._applicationIface = new DBus.Interface({ g_instance: this, g_connection: this._connection, g_object_path: '/org/mpris/MediaPlayer2', g_interface_info: MPRISIface, }); this._playerIface = new DBus.Interface({ g_instance: this, g_connection: this._connection, g_object_path: '/org/mpris/MediaPlayer2', g_interface_info: MPRISPlayerIface, }); } if (this._ownerId !== 0) return; const name = this.Identity.replace(/\W*/g, '_'); this._ownerId = Gio.bus_own_name_on_connection( this._connection, `org.mpris.MediaPlayer2.${name}`, Gio.BusNameOwnerFlags.NONE, null, null ); } unexport() { if (this._ownerId === 0) return; Gio.bus_unown_name(this._ownerId); this._ownerId = 0; } get Metadata() { if (this._Metadata === undefined) this._Metadata = {}; return this._Metadata; } Play() { printerr(`Play(): ${this.PlaybackStatus}`); if (this.PlaybackStatus === 'Playing') return; printerr('Play()'); this._PlaybackStatus = 'Playing'; this.notify('PlaybackStatus'); } Pause() { if (this.PlaybackStatus !== 'Playing') return; this._PlaybackStatus = 'Paused'; this.notify('PlaybackStatus'); } Seek(offset) { if (!this.CanSeek) return; this.emit('Seeked', offset); } destroy() { this.unexport(); if (this._connection) { this._connection.close(null, null); this._connection = null; this._applicationIface.destroy(); this._applicationIface = null; this._playerIface.destroy(); this._playerIface = null; } } }); gnome-shell-extension-gsconnect-50/installed-tests/fixtures/utils.js000066400000000000000000000237461421543444100262110ustar00rootroot00000000000000'use strict'; imports.gi.versions.Gdk = '3.0'; imports.gi.versions.Gtk = '3.0'; const ByteArray = imports.byteArray; const {Gio, GLib} = imports.gi; // Ensure the environment is prepared for testing const Config = imports.config; if (GLib.getenv('GSCONNECT_TEST')) { Config.PACKAGE_DATADIR = GLib.getenv('GJS_PATH'); Config.GSETTINGS_SCHEMA_DIR = GLib.getenv('GSETTINGS_SCHEMA_DIR'); } else { GLib.setenv('G_DEBUG', 'fatal-warnings,fatal-criticals', true); GLib.setenv('GSETTINGS_BACKEND', 'memory', true); GLib.setenv('NO_AT_BRIDGE', '1', true); imports.searchPath.unshift(Config.PACKAGE_DATADIR); } const {Device} = imports.service.device; const {Plugin} = imports.service.plugin; const {ChannelService} = imports.fixtures.backend; /* * File Helpers */ function get_datadir() { const thisPath = /@(.+):\d+/.exec((new Error()).stack.split('\n')[1])[1]; const thisFile = Gio.File.new_for_path(thisPath); return thisFile.get_parent().get_parent().get_child('data').get_path(); } const DATA_PATH = get_datadir(); function getDataPath(filename) { return GLib.build_filenamev([DATA_PATH, filename]); } function getDataUri(filename) { return `file://${getDataPath(filename)}`; } function getDataFile(filename) { return Gio.File.new_for_path(getDataPath(filename)); } function loadDataContents(filename) { const path = getDataPath(filename); const bytes = GLib.file_get_contents(path)[1]; return ByteArray.toString(bytes); } /* * Async Helpers */ Promise.idle = function (priority = GLib.PRIORITY_DEFAULT_IDLE) { return new Promise(resolve => { GLib.idle_add(priority, () => { resolve(); return GLib.SOURCE_REMOVE; }); }); }; /* * Identity Helpers */ function getDeviceType() { const types = [ 'desktop', 'laptop', 'phone', 'tablet', 'tv', ]; return types[Math.floor(Math.random() * types.length)]; } /** * Generate a pseudo-random device identity. * * @param {Object} params - Override parameters * @return {Object} A pseudo-random identity packet */ function generateIdentity(params = {}) { const identity = { 'id': Date.now(), 'type': 'kdeconnect.identity', 'body': { 'deviceId': GLib.uuid_string_random(), 'deviceName': 'Test Device', 'deviceType': getDeviceType(), 'protocolVersion': 7, 'incomingCapabilities': [], 'outgoingCapabilities': [], }, }; for (const [key, value] of Object.entries(params)) { if (key === 'body') Object.assign(identity.body, value); else identity[key] = value; } return identity; } /** * Check if @subset is a subset of @obj. * * @param {Object} obj - The haystack to compare with * @param {Object} subset - The needle to search for * @return {boolean} %true if the object is a subset */ function isSubset(obj, subset) { for (const [key, val] of Object.entries(subset)) { if (!obj.hasOwnProperty(key)) return false; // We were only checking for the key itself if (typeof val === 'undefined') continue; if (Array.isArray(val)) { // If passed an empty array, we're expecting it to be empty if (val.length === 0 && obj[key].length !== 0) return false; // Otherwise we're looking for a subset of the array if (!val.every(n => obj[key].includes(n))) return false; continue; } // This is JSON and KDE Connect has no %null use; an object is an object if (typeof val === 'object') { if (!isSubset(obj[key], val)) return false; continue; } if (obj[key] !== val) return false; } return true; } /** * Wait for the `handlePacket` method of a device or plugin to be passed a * packet to handle. Note, the object must have an active jasmine spy. * * @param {string} type - A KDE Connect packet type * @param {Object} [body] - Packet body properties */ async function _awaitPacket(type, body = null) { while (true) { for (const [packet] of this.handlePacket.calls.allArgs()) { if (packet.type !== type) continue; if (body === null) return; if (isSubset(packet.body, body)) return; } await Promise.idle(GLib.PRIORITY_DEFAULT); } } Device.prototype.awaitPacket = _awaitPacket; Plugin.prototype.awaitPacket = _awaitPacket; /** * Create temporary directories used by GSConnect. * * @return {string} The root temporary directory */ function isolateDirectories() { const Config = imports.config; const tmpdir = GLib.Dir.make_tmp('gsconnect.XXXXXX'); Config.CACHEDIR = GLib.build_filenamev([tmpdir, 'cache']); Config.CONFIGDIR = GLib.build_filenamev([tmpdir, 'config']); Config.RUNTIMEDIR = GLib.build_filenamev([tmpdir, 'runtime']); for (const path of [Config.CACHEDIR, Config.CONFIGDIR, Config.RUNTIMEDIR]) GLib.mkdir_with_parents(path, 0o755); return tmpdir; } /** * Patch in the mock components for plugin tests. */ function mockComponents() { const Components = imports.service.components; const MockComponents = imports.fixtures.components; Components.acquire = function (name) { return new MockComponents[name].Component(); }; Components.release = function (name) { return null; }; } /** * Recursively remove a directory. * * @param {Gio.File|string} file - The file or path to delete */ function removeDirectory(file) { try { if (typeof file === 'string') file = Gio.File.new_for_path(file); try { const iter = file.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null); let info; while ((info = iter.next_file(null))) removeDirectory(iter.get_child(info)); iter.close(null); } catch (e) { } file.delete(null); } catch (e) { } } /** * A test rig with two active GSconnectChannelService instances. */ var TestRig = class { /** * Create a new test rig. * * @param {boolean} [dirs] - Whether to isolate user directories */ constructor(dirs = true) { this.localService = new ChannelService({id: 'local-service'}); this.localDevice = null; this.localChannel = null; this.remoteService = new ChannelService({id: 'remote-service'}); this.remoteDevice = null; this.remoteChannel = null; if (dirs) this._tmpdir = isolateDirectories(); } /** * Prepare two devices and channels with appropriate capabilities. * * Connect the devices with `setConnected()`, pair them with `setPaired()` * and load their plugins with `loadPlugins()`. * * @param {Object} [overrides] - Capability overrides * @param {Object} overrides.localDevice - Local device overrides * @param {Object} overrides.remoteDevice - Remote device overrides * @return {Promise} A promise for the operation */ prepare(overrides = {}) { return new Promise((resolve, reject) => { const localId = this.localService.connect('channel', (service, channel) => { service.disconnect(localId); if (overrides.localDevice) Object.assign(channel.identity.body, overrides.localDevice); this.localChannel = channel; this.localDevice = new Device(channel.identity); if (this.localDevice && this.remoteDevice) resolve(); return true; }); const remoteId = this.remoteService.connect('channel', (service, channel) => { service.disconnect(remoteId); if (overrides.remoteDevice) Object.assign(channel.identity.body, overrides.remoteDevice); this.remoteChannel = channel; this.remoteDevice = new Device(channel.identity); if (this.localDevice && this.remoteDevice) resolve(); return true; }); this.localService.start(); this.remoteService.start(); this.localService.broadcast(`127.0.0.1:${this.remoteService.port}`); }); } /** * Set both devices as connected by applying the negotiated channels. * * @param {boolean} connected - %true to connect, %false to disconnect */ async setConnected(connected) { if (connected) { this.localDevice.setChannel(this.localChannel); this.remoteDevice.setChannel(this.remoteChannel); } else { this.localDevice.setChannel(null); this.remoteDevice.setChannel(null); } await Promise.idle(); } /** * Set both devices as paired by calling the internal setters. * * @param {boolean} paired - %true to pair, %false to unpair */ setPaired(paired) { this.localDevice._setPaired(paired); this.remoteDevice._setPaired(paired); } async loadPlugins() { await this.localDevice._loadPlugins(); await this.remoteDevice._loadPlugins(); } destroy() { // Local Device if (this.localDevice) this.localDevice.destroy(); if (this.localChannel) this.localChannel.close(); // Remote Device if (this.remoteDevice) this.remoteDevice.destroy(); if (this.remoteChannel) this.remoteChannel.close(); // Channel Services if (this.localService) this.localService.destroy(); if (this.remoteService) this.remoteService.destroy(); // Cleanup temporary files if (this._tmpdir) removeDirectory(this._tmpdir); } }; gnome-shell-extension-gsconnect-50/installed-tests/jasmine.js000066400000000000000000007646341421543444100246360ustar00rootroot00000000000000/* eslint-disable */ /* Copyright (c) 2008-2020 Pivotal Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // eslint-disable-next-line no-unused-vars var getJasmineRequireObj = (function(jasmineGlobal) { var jasmineRequire; if ( typeof module !== 'undefined' && module.exports && typeof exports !== 'undefined' ) { if (typeof global !== 'undefined') { jasmineGlobal = global; } else { jasmineGlobal = {}; } jasmineRequire = exports; } else { if ( typeof window !== 'undefined' && typeof window.toString === 'function' && window.toString() === '[object GjsGlobal]' ) { jasmineGlobal = window; } jasmineRequire = jasmineGlobal.jasmineRequire = {}; } function getJasmineRequire() { return jasmineRequire; } getJasmineRequire().core = function(jRequire) { var j$ = {}; jRequire.base(j$, jasmineGlobal); j$.util = jRequire.util(j$); j$.errors = jRequire.errors(); j$.formatErrorMsg = jRequire.formatErrorMsg(); j$.Any = jRequire.Any(j$); j$.Anything = jRequire.Anything(j$); j$.CallTracker = jRequire.CallTracker(j$); j$.MockDate = jRequire.MockDate(); j$.getClearStack = jRequire.clearStack(j$); j$.Clock = jRequire.Clock(); j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(j$); j$.Env = jRequire.Env(j$); j$.StackTrace = jRequire.StackTrace(j$); j$.ExceptionFormatter = jRequire.ExceptionFormatter(j$); j$.ExpectationFilterChain = jRequire.ExpectationFilterChain(); j$.Expector = jRequire.Expector(j$); j$.Expectation = jRequire.Expectation(j$); j$.buildExpectationResult = jRequire.buildExpectationResult(j$); j$.JsApiReporter = jRequire.JsApiReporter(j$); j$.asymmetricEqualityTesterArgCompatShim = jRequire.asymmetricEqualityTesterArgCompatShim( j$ ); j$.makePrettyPrinter = jRequire.makePrettyPrinter(j$); j$.pp = j$.makePrettyPrinter(); j$.MatchersUtil = jRequire.MatchersUtil(j$); j$.matchersUtil = new j$.MatchersUtil({ customTesters: [], pp: j$.pp }); j$.ObjectContaining = jRequire.ObjectContaining(j$); j$.ArrayContaining = jRequire.ArrayContaining(j$); j$.ArrayWithExactContents = jRequire.ArrayWithExactContents(j$); j$.MapContaining = jRequire.MapContaining(j$); j$.SetContaining = jRequire.SetContaining(j$); j$.QueueRunner = jRequire.QueueRunner(j$); j$.ReportDispatcher = jRequire.ReportDispatcher(j$); j$.Spec = jRequire.Spec(j$); j$.Spy = jRequire.Spy(j$); j$.SpyFactory = jRequire.SpyFactory(j$); j$.SpyRegistry = jRequire.SpyRegistry(j$); j$.SpyStrategy = jRequire.SpyStrategy(j$); j$.StringMatching = jRequire.StringMatching(j$); j$.UserContext = jRequire.UserContext(j$); j$.Suite = jRequire.Suite(j$); j$.Timer = jRequire.Timer(); j$.TreeProcessor = jRequire.TreeProcessor(); j$.version = jRequire.version(); j$.Order = jRequire.Order(); j$.DiffBuilder = jRequire.DiffBuilder(j$); j$.NullDiffBuilder = jRequire.NullDiffBuilder(j$); j$.ObjectPath = jRequire.ObjectPath(j$); j$.MismatchTree = jRequire.MismatchTree(j$); j$.GlobalErrors = jRequire.GlobalErrors(j$); j$.Truthy = jRequire.Truthy(j$); j$.Falsy = jRequire.Falsy(j$); j$.Empty = jRequire.Empty(j$); j$.NotEmpty = jRequire.NotEmpty(j$); j$.matchers = jRequire.requireMatchers(jRequire, j$); j$.asyncMatchers = jRequire.requireAsyncMatchers(jRequire, j$); return j$; }; return getJasmineRequire; })(this); getJasmineRequireObj().requireMatchers = function(jRequire, j$) { var availableMatchers = [ 'nothing', 'toBe', 'toBeCloseTo', 'toBeDefined', 'toBeInstanceOf', 'toBeFalse', 'toBeFalsy', 'toBeGreaterThan', 'toBeGreaterThanOrEqual', 'toBeLessThan', 'toBeLessThanOrEqual', 'toBeNaN', 'toBeNegativeInfinity', 'toBeNull', 'toBePositiveInfinity', 'toBeTrue', 'toBeTruthy', 'toBeUndefined', 'toContain', 'toEqual', 'toHaveSize', 'toHaveBeenCalled', 'toHaveBeenCalledBefore', 'toHaveBeenCalledOnceWith', 'toHaveBeenCalledTimes', 'toHaveBeenCalledWith', 'toHaveClass', 'toMatch', 'toThrow', 'toThrowError', 'toThrowMatching' ], matchers = {}; for (var i = 0; i < availableMatchers.length; i++) { var name = availableMatchers[i]; matchers[name] = jRequire[name](j$); } return matchers; }; getJasmineRequireObj().base = function(j$, jasmineGlobal) { j$.unimplementedMethod_ = function() { throw new Error('unimplemented method'); }; /** * Maximum object depth the pretty printer will print to. * Set this to a lower value to speed up pretty printing if you have large objects. * @name jasmine.MAX_PRETTY_PRINT_DEPTH * @since 1.3.0 */ j$.MAX_PRETTY_PRINT_DEPTH = 8; /** * Maximum number of array elements to display when pretty printing objects. * This will also limit the number of keys and values displayed for an object. * Elements past this number will be ellipised. * @name jasmine.MAX_PRETTY_PRINT_ARRAY_LENGTH * @since 2.7.0 */ j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 50; /** * Maximum number of characters to display when pretty printing objects. * Characters past this number will be ellipised. * @name jasmine.MAX_PRETTY_PRINT_CHARS * @since 2.9.0 */ j$.MAX_PRETTY_PRINT_CHARS = 1000; /** * Default number of milliseconds Jasmine will wait for an asynchronous spec to complete. * @name jasmine.DEFAULT_TIMEOUT_INTERVAL * @since 1.3.0 */ j$.DEFAULT_TIMEOUT_INTERVAL = 5000; j$.getGlobal = function() { return jasmineGlobal; }; /** * Get the currently booted Jasmine Environment. * * @name jasmine.getEnv * @since 1.3.0 * @function * @return {Env} */ j$.getEnv = function(options) { var env = (j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options)); //jasmine. singletons in here (setTimeout blah blah). return env; }; j$.isArray_ = function(value) { return j$.isA_('Array', value); }; j$.isObject_ = function(value) { return ( !j$.util.isUndefined(value) && value !== null && j$.isA_('Object', value) ); }; j$.isString_ = function(value) { return j$.isA_('String', value); }; j$.isNumber_ = function(value) { return j$.isA_('Number', value); }; j$.isFunction_ = function(value) { return j$.isA_('Function', value); }; j$.isAsyncFunction_ = function(value) { return j$.isA_('AsyncFunction', value); }; j$.isTypedArray_ = function(value) { return ( j$.isA_('Float32Array', value) || j$.isA_('Float64Array', value) || j$.isA_('Int16Array', value) || j$.isA_('Int32Array', value) || j$.isA_('Int8Array', value) || j$.isA_('Uint16Array', value) || j$.isA_('Uint32Array', value) || j$.isA_('Uint8Array', value) || j$.isA_('Uint8ClampedArray', value) ); }; j$.isA_ = function(typeName, value) { return j$.getType_(value) === '[object ' + typeName + ']'; }; j$.isError_ = function(value) { if (value instanceof Error) { return true; } if (value && value.constructor && value.constructor.constructor) { var valueGlobal = value.constructor.constructor('return this'); if (j$.isFunction_(valueGlobal)) { valueGlobal = valueGlobal(); } if (valueGlobal.Error && value instanceof valueGlobal.Error) { return true; } } return false; }; j$.isAsymmetricEqualityTester_ = function(obj) { return obj && obj.asymmetricMatch ? j$.isA_('Function', obj.asymmetricMatch) : false; }; j$.getType_ = function(value) { return Object.prototype.toString.apply(value); }; j$.isDomNode = function(obj) { // Node is a function, because constructors return typeof jasmineGlobal.Node !== 'undefined' ? obj instanceof jasmineGlobal.Node : obj !== null && typeof obj === 'object' && typeof obj.nodeType === 'number' && typeof obj.nodeName === 'string'; // return obj.nodeType > 0; }; j$.isMap = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && typeof jasmineGlobal.Map !== 'undefined' && obj.constructor === jasmineGlobal.Map ); }; j$.isSet = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && typeof jasmineGlobal.Set !== 'undefined' && obj.constructor === jasmineGlobal.Set ); }; j$.isWeakMap = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && typeof jasmineGlobal.WeakMap !== 'undefined' && obj.constructor === jasmineGlobal.WeakMap ); }; j$.isDataView = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && typeof jasmineGlobal.DataView !== 'undefined' && obj.constructor === jasmineGlobal.DataView ); }; j$.isPromise = function(obj) { return ( typeof jasmineGlobal.Promise !== 'undefined' && !!obj && obj.constructor === jasmineGlobal.Promise ); }; j$.isPromiseLike = function(obj) { return !!obj && j$.isFunction_(obj.then); }; j$.fnNameFor = function(func) { if (func.name) { return func.name; } var matches = func.toString().match(/^\s*function\s*(\w+)\s*\(/) || func.toString().match(/^\s*\[object\s*(\w+)Constructor\]/); return matches ? matches[1] : ''; }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is an instance of the specified class/constructor. * @name jasmine.any * @since 1.3.0 * @function * @param {Constructor} clazz - The constructor to check against. */ j$.any = function(clazz) { return new j$.Any(clazz); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is not `null` and not `undefined`. * @name jasmine.anything * @since 2.2.0 * @function */ j$.anything = function() { return new j$.Anything(); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is `true` or anything truthy. * @name jasmine.truthy * @since 3.1.0 * @function */ j$.truthy = function() { return new j$.Truthy(); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is `null`, `undefined`, `0`, `false` or anything falsey. * @name jasmine.falsy * @since 3.1.0 * @function */ j$.falsy = function() { return new j$.Falsy(); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is empty. * @name jasmine.empty * @since 3.1.0 * @function */ j$.empty = function() { return new j$.Empty(); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is not empty. * @name jasmine.notEmpty * @since 3.1.0 * @function */ j$.notEmpty = function() { return new j$.NotEmpty(); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared contains at least the keys and values. * @name jasmine.objectContaining * @since 1.3.0 * @function * @param {Object} sample - The subset of properties that _must_ be in the actual. */ j$.objectContaining = function(sample) { return new j$.ObjectContaining(sample); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value is a `String` that matches the `RegExp` or `String`. * @name jasmine.stringMatching * @since 2.2.0 * @function * @param {RegExp|String} expected */ j$.stringMatching = function(expected) { return new j$.StringMatching(expected); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value is an `Array` that contains at least the elements in the sample. * @name jasmine.arrayContaining * @since 2.2.0 * @function * @param {Array} sample */ j$.arrayContaining = function(sample) { return new j$.ArrayContaining(sample); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value is an `Array` that contains all of the elements in the sample in any order. * @name jasmine.arrayWithExactContents * @since 2.8.0 * @function * @param {Array} sample */ j$.arrayWithExactContents = function(sample) { return new j$.ArrayWithExactContents(sample); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if every key/value pair in the sample passes the deep equality comparison * with at least one key/value pair in the actual value being compared * @name jasmine.mapContaining * @since 3.5.0 * @function * @param {Map} sample - The subset of items that _must_ be in the actual. */ j$.mapContaining = function(sample) { return new j$.MapContaining(sample); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if every item in the sample passes the deep equality comparison * with at least one item in the actual value being compared * @name jasmine.setContaining * @since 3.5.0 * @function * @param {Set} sample - The subset of items that _must_ be in the actual. */ j$.setContaining = function(sample) { return new j$.SetContaining(sample); }; j$.isSpy = function(putativeSpy) { if (!putativeSpy || !putativeSpy.and) { return false; } return ( putativeSpy.and instanceof j$.SpyStrategy && putativeSpy.calls instanceof j$.CallTracker ); }; }; getJasmineRequireObj().util = function(j$) { var util = {}; util.inherit = function(childClass, parentClass) { var Subclass = function() {}; Subclass.prototype = parentClass.prototype; childClass.prototype = new Subclass(); }; util.htmlEscape = function(str) { if (!str) { return str; } return str .replace(/&/g, '&') .replace(//g, '>'); }; util.argsToArray = function(args) { var arrayOfArgs = []; for (var i = 0; i < args.length; i++) { arrayOfArgs.push(args[i]); } return arrayOfArgs; }; util.isUndefined = function(obj) { return obj === void 0; }; util.arrayContains = function(array, search) { var i = array.length; while (i--) { if (array[i] === search) { return true; } } return false; }; util.clone = function(obj) { if (Object.prototype.toString.apply(obj) === '[object Array]') { return obj.slice(); } var cloned = {}; for (var prop in obj) { if (obj.hasOwnProperty(prop)) { cloned[prop] = obj[prop]; } } return cloned; }; util.cloneArgs = function(args) { var clonedArgs = []; var argsAsArray = j$.util.argsToArray(args); for (var i = 0; i < argsAsArray.length; i++) { var str = Object.prototype.toString.apply(argsAsArray[i]), primitives = /^\[object (Boolean|String|RegExp|Number)/; // All falsey values are either primitives, `null`, or `undefined. if (!argsAsArray[i] || str.match(primitives)) { clonedArgs.push(argsAsArray[i]); } else { clonedArgs.push(j$.util.clone(argsAsArray[i])); } } return clonedArgs; }; util.getPropertyDescriptor = function(obj, methodName) { var descriptor, proto = obj; do { descriptor = Object.getOwnPropertyDescriptor(proto, methodName); proto = Object.getPrototypeOf(proto); } while (!descriptor && proto); return descriptor; }; util.objectDifference = function(obj, toRemove) { var diff = {}; for (var key in obj) { if (util.has(obj, key) && !util.has(toRemove, key)) { diff[key] = obj[key]; } } return diff; }; util.has = function(obj, key) { return Object.prototype.hasOwnProperty.call(obj, key); }; util.errorWithStack = function errorWithStack() { // Don't throw and catch if we don't have to, because it makes it harder // for users to debug their code with exception breakpoints. var error = new Error(); if (error.stack) { return error; } // But some browsers (e.g. Phantom) only provide a stack trace if we throw. try { throw new Error(); } catch (e) { return e; } }; function callerFile() { var trace = new j$.StackTrace(util.errorWithStack()); return trace.frames[2].file; } util.jasmineFile = (function() { var result; return function() { if (!result) { result = callerFile(); } return result; }; })(); function StopIteration() {} StopIteration.prototype = Object.create(Error.prototype); StopIteration.prototype.constructor = StopIteration; // useful for maps and sets since `forEach` is the only IE11-compatible way to iterate them util.forEachBreakable = function(iterable, iteratee) { function breakLoop() { throw new StopIteration(); } try { iterable.forEach(function(value, key) { iteratee(breakLoop, value, key, iterable); }); } catch (error) { if (!(error instanceof StopIteration)) throw error; } }; return util; }; getJasmineRequireObj().Spec = function(j$) { function Spec(attrs) { this.expectationFactory = attrs.expectationFactory; this.asyncExpectationFactory = attrs.asyncExpectationFactory; this.resultCallback = attrs.resultCallback || function() {}; this.id = attrs.id; this.description = attrs.description || ''; this.queueableFn = attrs.queueableFn; this.beforeAndAfterFns = attrs.beforeAndAfterFns || function() { return { befores: [], afters: [] }; }; this.userContext = attrs.userContext || function() { return {}; }; this.onStart = attrs.onStart || function() {}; this.getSpecName = attrs.getSpecName || function() { return ''; }; this.expectationResultFactory = attrs.expectationResultFactory || function() {}; this.queueRunnerFactory = attrs.queueRunnerFactory || function() {}; this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; this.timer = attrs.timer || new j$.Timer(); if (!this.queueableFn.fn) { this.pend(); } /** * @typedef SpecResult * @property {Int} id - The unique id of this spec. * @property {String} description - The description passed to the {@link it} that created this spec. * @property {String} fullName - The full description including all ancestors of this spec. * @property {Expectation[]} failedExpectations - The list of expectations that failed during execution of this spec. * @property {Expectation[]} passedExpectations - The list of expectations that passed during execution of this spec. * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec. * @property {String} pendingReason - If the spec is {@link pending}, this will be the reason. * @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec. * @property {number} duration - The time in ms used by the spec execution, including any before/afterEach. * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty} */ this.result = { id: this.id, description: this.description, fullName: this.getFullName(), failedExpectations: [], passedExpectations: [], deprecationWarnings: [], pendingReason: '', duration: null, properties: null }; } Spec.prototype.addExpectationResult = function(passed, data, isError) { var expectationResult = this.expectationResultFactory(data); if (passed) { this.result.passedExpectations.push(expectationResult); } else { this.result.failedExpectations.push(expectationResult); if (this.throwOnExpectationFailure && !isError) { throw new j$.errors.ExpectationFailed(); } } }; Spec.prototype.setSpecProperty = function(key, value) { this.result.properties = this.result.properties || {}; this.result.properties[key] = value; }; Spec.prototype.expect = function(actual) { return this.expectationFactory(actual, this); }; Spec.prototype.expectAsync = function(actual) { return this.asyncExpectationFactory(actual, this); }; Spec.prototype.execute = function(onComplete, excluded, failSpecWithNoExp) { var self = this; var onStart = { fn: function(done) { self.timer.start(); self.onStart(self, done); } }; var complete = { fn: function(done) { self.queueableFn.fn = null; self.result.status = self.status(excluded, failSpecWithNoExp); self.result.duration = self.timer.elapsed(); self.resultCallback(self.result, done); } }; var fns = this.beforeAndAfterFns(); var regularFns = fns.befores.concat(this.queueableFn); var runnerConfig = { isLeaf: true, queueableFns: regularFns, cleanupFns: fns.afters, onException: function() { self.onException.apply(self, arguments); }, onComplete: function() { onComplete( self.result.status === 'failed' && new j$.StopExecutionError('spec failed') ); }, userContext: this.userContext() }; if (this.markedPending || excluded === true) { runnerConfig.queueableFns = []; runnerConfig.cleanupFns = []; } runnerConfig.queueableFns.unshift(onStart); runnerConfig.cleanupFns.push(complete); this.queueRunnerFactory(runnerConfig); }; Spec.prototype.onException = function onException(e) { if (Spec.isPendingSpecException(e)) { this.pend(extractCustomPendingMessage(e)); return; } if (e instanceof j$.errors.ExpectationFailed) { return; } this.addExpectationResult( false, { matcherName: '', passed: false, expected: '', actual: '', error: e }, true ); }; Spec.prototype.pend = function(message) { this.markedPending = true; if (message) { this.result.pendingReason = message; } }; Spec.prototype.getResult = function() { this.result.status = this.status(); return this.result; }; Spec.prototype.status = function(excluded, failSpecWithNoExpectations) { if (excluded === true) { return 'excluded'; } if (this.markedPending) { return 'pending'; } if ( this.result.failedExpectations.length > 0 || (failSpecWithNoExpectations && this.result.failedExpectations.length + this.result.passedExpectations.length === 0) ) { return 'failed'; } return 'passed'; }; Spec.prototype.getFullName = function() { return this.getSpecName(this); }; Spec.prototype.addDeprecationWarning = function(deprecation) { if (typeof deprecation === 'string') { deprecation = { message: deprecation }; } this.result.deprecationWarnings.push( this.expectationResultFactory(deprecation) ); }; var extractCustomPendingMessage = function(e) { var fullMessage = e.toString(), boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage), boilerplateEnd = boilerplateStart + Spec.pendingSpecExceptionMessage.length; return fullMessage.substr(boilerplateEnd); }; Spec.pendingSpecExceptionMessage = '=> marked Pending'; Spec.isPendingSpecException = function(e) { return !!( e && e.toString && e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1 ); }; return Spec; }; if (typeof window == void 0 && typeof exports == 'object') { /* globals exports */ exports.Spec = jasmineRequire.Spec; } /*jshint bitwise: false*/ getJasmineRequireObj().Order = function() { function Order(options) { this.random = 'random' in options ? options.random : true; var seed = (this.seed = options.seed || generateSeed()); this.sort = this.random ? randomOrder : naturalOrder; function naturalOrder(items) { return items; } function randomOrder(items) { var copy = items.slice(); copy.sort(function(a, b) { return jenkinsHash(seed + a.id) - jenkinsHash(seed + b.id); }); return copy; } function generateSeed() { return String(Math.random()).slice(-5); } // Bob Jenkins One-at-a-Time Hash algorithm is a non-cryptographic hash function // used to get a different output when the key changes slightly. // We use your return to sort the children randomly in a consistent way when // used in conjunction with a seed function jenkinsHash(key) { var hash, i; for (hash = i = 0; i < key.length; ++i) { hash += key.charCodeAt(i); hash += hash << 10; hash ^= hash >> 6; } hash += hash << 3; hash ^= hash >> 11; hash += hash << 15; return hash; } } return Order; }; getJasmineRequireObj().Env = function(j$) { /** * _Note:_ Do not construct this directly, Jasmine will make one during booting. * @name Env * @since 2.0.0 * @classdesc The Jasmine environment * @constructor */ function Env(options) { options = options || {}; var self = this; var global = options.global || j$.getGlobal(); var customPromise; var totalSpecsDefined = 0; var realSetTimeout = global.setTimeout; var realClearTimeout = global.clearTimeout; var clearStack = j$.getClearStack(global); this.clock = new j$.Clock( global, function() { return new j$.DelayedFunctionScheduler(); }, new j$.MockDate(global) ); var runnableResources = {}; var currentSpec = null; var currentlyExecutingSuites = []; var currentDeclarationSuite = null; var hasFailures = false; /** * This represents the available options to configure Jasmine. * Options that are not provided will use their default values * @interface Configuration * @since 3.3.0 */ var config = { /** * Whether to randomize spec execution order * @name Configuration#random * @since 3.3.0 * @type Boolean * @default false */ random: false, /** * Seed to use as the basis of randomization. * Null causes the seed to be determined randomly at the start of execution. * @name Configuration#seed * @since 3.3.0 * @type function * @default null */ seed: null, /** * Whether to stop execution of the suite after the first spec failure * @name Configuration#failFast * @since 3.3.0 * @type Boolean * @default false */ failFast: false, /** * Whether to fail the spec if it ran no expectations. By default * a spec that ran no expectations is reported as passed. Setting this * to true will report such spec as a failure. * @name Configuration#failSpecWithNoExpectations * @since 3.5.0 * @type Boolean * @default false */ failSpecWithNoExpectations: false, /** * Whether to cause specs to only have one expectation failure. * @name Configuration#oneFailurePerSpec * @since 3.3.0 * @type Boolean * @default false */ oneFailurePerSpec: false, /** * Function to use to filter specs * @name Configuration#specFilter * @since 3.3.0 * @type function * @default true */ specFilter: function() { return true; }, /** * Whether or not reporters should hide disabled specs from their output. * Currently only supported by Jasmine's HTMLReporter * @name Configuration#hideDisabled * @since 3.3.0 * @type Boolean * @default false */ hideDisabled: false, /** * Set to provide a custom promise library that Jasmine will use if it needs * to create a promise. If not set, it will default to whatever global Promise * library is available (if any). * @name Configuration#Promise * @since 3.5.0 * @type function * @default undefined */ Promise: undefined }; var currentSuite = function() { return currentlyExecutingSuites[currentlyExecutingSuites.length - 1] || undefined; }; var currentRunnable = function() { return currentSpec || currentSuite(); }; var globalErrors = null; var installGlobalErrors = function() { if (globalErrors) { return; } globalErrors = new j$.GlobalErrors(); globalErrors.install(); }; if (!options.suppressLoadErrors) { installGlobalErrors(); globalErrors.pushListener(function( message, filename, lineno, colNo, err ) { topSuite.result.failedExpectations.push({ passed: false, globalErrorType: 'load', message: message, stack: err && err.stack, filename: filename, lineno: lineno }); }); } /** * Configure your jasmine environment * @name Env#configure * @since 3.3.0 * @argument {Configuration} configuration * @function */ this.configure = function(configuration) { if (configuration.specFilter) { config.specFilter = configuration.specFilter; } if (configuration.hasOwnProperty('random')) { config.random = !!configuration.random; } if (configuration.hasOwnProperty('seed')) { config.seed = configuration.seed; } if (configuration.hasOwnProperty('failFast')) { config.failFast = configuration.failFast; } if (configuration.hasOwnProperty('failSpecWithNoExpectations')) { config.failSpecWithNoExpectations = configuration.failSpecWithNoExpectations; } if (configuration.hasOwnProperty('oneFailurePerSpec')) { config.oneFailurePerSpec = configuration.oneFailurePerSpec; } if (configuration.hasOwnProperty('hideDisabled')) { config.hideDisabled = configuration.hideDisabled; } // Don't use hasOwnProperty to check for Promise existence because Promise // can be initialized to undefined, either explicitly or by using the // object returned from Env#configuration. In particular, Karma does this. if (configuration.Promise) { if ( typeof configuration.Promise.resolve === 'function' && typeof configuration.Promise.reject === 'function' ) { customPromise = configuration.Promise; } else { throw new Error( 'Custom promise library missing `resolve`/`reject` functions' ); } } }; /** * Get the current configuration for your jasmine environment * @name Env#configuration * @since 3.3.0 * @function * @returns {Configuration} */ this.configuration = function() { var result = {}; for (var property in config) { result[property] = config[property]; } return result; }; Object.defineProperty(this, 'specFilter', { get: function() { self.deprecated( 'Getting specFilter directly from Env is deprecated and will be removed in a future version of Jasmine, please check the specFilter option from `configuration`' ); return config.specFilter; }, set: function(val) { self.deprecated( 'Setting specFilter directly on Env is deprecated and will be removed in a future version of Jasmine, please use the specFilter option in `configure`' ); config.specFilter = val; } }); this.setDefaultSpyStrategy = function(defaultStrategyFn) { if (!currentRunnable()) { throw new Error( 'Default spy strategy must be set in a before function or a spec' ); } runnableResources[ currentRunnable().id ].defaultStrategyFn = defaultStrategyFn; }; this.addSpyStrategy = function(name, fn) { if (!currentRunnable()) { throw new Error( 'Custom spy strategies must be added in a before function or a spec' ); } runnableResources[currentRunnable().id].customSpyStrategies[name] = fn; }; this.addCustomEqualityTester = function(tester) { if (!currentRunnable()) { throw new Error( 'Custom Equalities must be added in a before function or a spec' ); } runnableResources[currentRunnable().id].customEqualityTesters.push( tester ); }; this.addMatchers = function(matchersToAdd) { if (!currentRunnable()) { throw new Error( 'Matchers must be added in a before function or a spec' ); } var customMatchers = runnableResources[currentRunnable().id].customMatchers; for (var matcherName in matchersToAdd) { customMatchers[matcherName] = matchersToAdd[matcherName]; } }; this.addAsyncMatchers = function(matchersToAdd) { if (!currentRunnable()) { throw new Error( 'Async Matchers must be added in a before function or a spec' ); } var customAsyncMatchers = runnableResources[currentRunnable().id].customAsyncMatchers; for (var matcherName in matchersToAdd) { customAsyncMatchers[matcherName] = matchersToAdd[matcherName]; } }; this.addCustomObjectFormatter = function(formatter) { if (!currentRunnable()) { throw new Error( 'Custom object formatters must be added in a before function or a spec' ); } runnableResources[currentRunnable().id].customObjectFormatters.push( formatter ); }; j$.Expectation.addCoreMatchers(j$.matchers); j$.Expectation.addAsyncCoreMatchers(j$.asyncMatchers); var nextSpecId = 0; var getNextSpecId = function() { return 'spec' + nextSpecId++; }; var nextSuiteId = 0; var getNextSuiteId = function() { return 'suite' + nextSuiteId++; }; var makePrettyPrinter = function() { var customObjectFormatters = runnableResources[currentRunnable().id].customObjectFormatters; return j$.makePrettyPrinter(customObjectFormatters); }; var makeMatchersUtil = function() { var customEqualityTesters = runnableResources[currentRunnable().id].customEqualityTesters; return new j$.MatchersUtil({ customTesters: customEqualityTesters, pp: makePrettyPrinter() }); }; var expectationFactory = function(actual, spec) { var customEqualityTesters = runnableResources[spec.id].customEqualityTesters; return j$.Expectation.factory({ matchersUtil: makeMatchersUtil(), customEqualityTesters: customEqualityTesters, customMatchers: runnableResources[spec.id].customMatchers, actual: actual, addExpectationResult: addExpectationResult }); function addExpectationResult(passed, result) { return spec.addExpectationResult(passed, result); } }; function recordLateExpectation(runable, runableType, result) { var delayedExpectationResult = {}; Object.keys(result).forEach(function(k) { delayedExpectationResult[k] = result[k]; }); delayedExpectationResult.passed = false; delayedExpectationResult.globalErrorType = 'lateExpectation'; delayedExpectationResult.message = runableType + ' "' + runable.getFullName() + '" ran a "' + result.matcherName + '" expectation after it finished.\n'; if (result.message) { delayedExpectationResult.message += 'Message: "' + result.message + '"\n'; } delayedExpectationResult.message += 'Did you forget to return or await the result of expectAsync?'; topSuite.result.failedExpectations.push(delayedExpectationResult); } var asyncExpectationFactory = function(actual, spec, runableType) { return j$.Expectation.asyncFactory({ matchersUtil: makeMatchersUtil(), customEqualityTesters: runnableResources[spec.id].customEqualityTesters, customAsyncMatchers: runnableResources[spec.id].customAsyncMatchers, actual: actual, addExpectationResult: addExpectationResult }); function addExpectationResult(passed, result) { if (currentRunnable() !== spec) { recordLateExpectation(spec, runableType, result); } return spec.addExpectationResult(passed, result); } }; var suiteAsyncExpectationFactory = function(actual, suite) { return asyncExpectationFactory(actual, suite, 'Suite'); }; var specAsyncExpectationFactory = function(actual, suite) { return asyncExpectationFactory(actual, suite, 'Spec'); }; var defaultResourcesForRunnable = function(id, parentRunnableId) { var resources = { spies: [], customEqualityTesters: [], customMatchers: {}, customAsyncMatchers: {}, customSpyStrategies: {}, defaultStrategyFn: undefined, customObjectFormatters: [] }; if (runnableResources[parentRunnableId]) { resources.customEqualityTesters = j$.util.clone( runnableResources[parentRunnableId].customEqualityTesters ); resources.customMatchers = j$.util.clone( runnableResources[parentRunnableId].customMatchers ); resources.customAsyncMatchers = j$.util.clone( runnableResources[parentRunnableId].customAsyncMatchers ); resources.defaultStrategyFn = runnableResources[parentRunnableId].defaultStrategyFn; } runnableResources[id] = resources; }; var clearResourcesForRunnable = function(id) { spyRegistry.clearSpies(); delete runnableResources[id]; }; var beforeAndAfterFns = function(suite) { return function() { var befores = [], afters = []; while (suite) { befores = befores.concat(suite.beforeFns); afters = afters.concat(suite.afterFns); suite = suite.parentSuite; } return { befores: befores.reverse(), afters: afters }; }; }; var getSpecName = function(spec, suite) { var fullName = [spec.description], suiteFullName = suite.getFullName(); if (suiteFullName !== '') { fullName.unshift(suiteFullName); } return fullName.join(' '); }; // TODO: we may just be able to pass in the fn instead of wrapping here var buildExpectationResult = j$.buildExpectationResult, exceptionFormatter = new j$.ExceptionFormatter(), expectationResultFactory = function(attrs) { attrs.messageFormatter = exceptionFormatter.message; attrs.stackFormatter = exceptionFormatter.stack; return buildExpectationResult(attrs); }; /** * Sets whether Jasmine should throw an Error when an expectation fails. * This causes a spec to only have one expectation failure. * @name Env#throwOnExpectationFailure * @since 2.3.0 * @function * @param {Boolean} value Whether to throw when a expectation fails * @deprecated Use the `oneFailurePerSpec` option with {@link Env#configure} */ this.throwOnExpectationFailure = function(value) { this.deprecated( 'Setting throwOnExpectationFailure directly on Env is deprecated and will be removed in a future version of Jasmine, please use the oneFailurePerSpec option in `configure`' ); this.configure({ oneFailurePerSpec: !!value }); }; this.throwingExpectationFailures = function() { this.deprecated( 'Getting throwingExpectationFailures directly from Env is deprecated and will be removed in a future version of Jasmine, please check the oneFailurePerSpec option from `configuration`' ); return config.oneFailurePerSpec; }; /** * Set whether to stop suite execution when a spec fails * @name Env#stopOnSpecFailure * @since 2.7.0 * @function * @param {Boolean} value Whether to stop suite execution when a spec fails * @deprecated Use the `failFast` option with {@link Env#configure} */ this.stopOnSpecFailure = function(value) { this.deprecated( 'Setting stopOnSpecFailure directly is deprecated and will be removed in a future version of Jasmine, please use the failFast option in `configure`' ); this.configure({ failFast: !!value }); }; this.stoppingOnSpecFailure = function() { this.deprecated( 'Getting stoppingOnSpecFailure directly from Env is deprecated and will be removed in a future version of Jasmine, please check the failFast option from `configuration`' ); return config.failFast; }; /** * Set whether to randomize test execution order * @name Env#randomizeTests * @since 2.4.0 * @function * @param {Boolean} value Whether to randomize execution order * @deprecated Use the `random` option with {@link Env#configure} */ this.randomizeTests = function(value) { this.deprecated( 'Setting randomizeTests directly is deprecated and will be removed in a future version of Jasmine, please use the random option in `configure`' ); config.random = !!value; }; this.randomTests = function() { this.deprecated( 'Getting randomTests directly from Env is deprecated and will be removed in a future version of Jasmine, please check the random option from `configuration`' ); return config.random; }; /** * Set the random number seed for spec randomization * @name Env#seed * @since 2.4.0 * @function * @param {Number} value The seed value * @deprecated Use the `seed` option with {@link Env#configure} */ this.seed = function(value) { this.deprecated( 'Setting seed directly is deprecated and will be removed in a future version of Jasmine, please use the seed option in `configure`' ); if (value) { config.seed = value; } return config.seed; }; this.hidingDisabled = function(value) { this.deprecated( 'Getting hidingDisabled directly from Env is deprecated and will be removed in a future version of Jasmine, please check the hideDisabled option from `configuration`' ); return config.hideDisabled; }; /** * @name Env#hideDisabled * @since 3.2.0 * @function */ this.hideDisabled = function(value) { this.deprecated( 'Setting hideDisabled directly is deprecated and will be removed in a future version of Jasmine, please use the hideDisabled option in `configure`' ); config.hideDisabled = !!value; }; this.deprecated = function(deprecation) { var runnable = currentRunnable() || topSuite; runnable.addDeprecationWarning(deprecation); if ( typeof console !== 'undefined' && typeof console.error === 'function' ) { console.error('DEPRECATION:', deprecation); } }; var queueRunnerFactory = function(options, args) { var failFast = false; if (options.isLeaf) { failFast = config.oneFailurePerSpec; } else if (!options.isReporter) { failFast = config.failFast; } options.clearStack = options.clearStack || clearStack; options.timeout = { setTimeout: realSetTimeout, clearTimeout: realClearTimeout }; options.fail = self.fail; options.globalErrors = globalErrors; options.completeOnFirstError = failFast; options.onException = options.onException || function(e) { (currentRunnable() || topSuite).onException(e); }; options.deprecated = self.deprecated; new j$.QueueRunner(options).execute(args); }; var topSuite = new j$.Suite({ env: this, id: getNextSuiteId(), description: 'Jasmine__TopLevel__Suite', expectationFactory: expectationFactory, asyncExpectationFactory: suiteAsyncExpectationFactory, expectationResultFactory: expectationResultFactory }); defaultResourcesForRunnable(topSuite.id); currentDeclarationSuite = topSuite; this.topSuite = function() { return topSuite; }; /** * This represents the available reporter callback for an object passed to {@link Env#addReporter}. * @interface Reporter * @see custom_reporter */ var reporter = new j$.ReportDispatcher( [ /** * `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts. * @function * @name Reporter#jasmineStarted * @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'jasmineStarted', /** * When the entire suite has finished execution `jasmineDone` is called * @function * @name Reporter#jasmineDone * @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running. * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'jasmineDone', /** * `suiteStarted` is invoked when a `describe` starts to run * @function * @name Reporter#suiteStarted * @param {SuiteResult} result Information about the individual {@link describe} being run * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'suiteStarted', /** * `suiteDone` is invoked when all of the child specs and suites for a given suite have been run * * While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`. * @function * @name Reporter#suiteDone * @param {SuiteResult} result * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'suiteDone', /** * `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions) * @function * @name Reporter#specStarted * @param {SpecResult} result Information about the individual {@link it} being run * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'specStarted', /** * `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run. * * While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed. * @function * @name Reporter#specDone * @param {SpecResult} result * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'specDone' ], queueRunnerFactory ); this.execute = function(runnablesToRun) { installGlobalErrors(); if (!runnablesToRun) { if (focusedRunnables.length) { runnablesToRun = focusedRunnables; } else { runnablesToRun = [topSuite.id]; } } var order = new j$.Order({ random: config.random, seed: config.seed }); var processor = new j$.TreeProcessor({ tree: topSuite, runnableIds: runnablesToRun, queueRunnerFactory: queueRunnerFactory, failSpecWithNoExpectations: config.failSpecWithNoExpectations, nodeStart: function(suite, next) { currentlyExecutingSuites.push(suite); defaultResourcesForRunnable(suite.id, suite.parentSuite.id); reporter.suiteStarted(suite.result, next); suite.startTimer(); }, nodeComplete: function(suite, result, next) { if (suite !== currentSuite()) { throw new Error('Tried to complete the wrong suite'); } clearResourcesForRunnable(suite.id); currentlyExecutingSuites.pop(); if (result.status === 'failed') { hasFailures = true; } suite.endTimer(); reporter.suiteDone(result, next); }, orderChildren: function(node) { return order.sort(node.children); }, excludeNode: function(spec) { return !config.specFilter(spec); } }); if (!processor.processTree().valid) { throw new Error( 'Invalid order: would cause a beforeAll or afterAll to be run multiple times' ); } var jasmineTimer = new j$.Timer(); jasmineTimer.start(); /** * Information passed to the {@link Reporter#jasmineStarted} event. * @typedef JasmineStartedInfo * @property {Int} totalSpecsDefined - The total number of specs defined in this suite. * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. */ reporter.jasmineStarted( { totalSpecsDefined: totalSpecsDefined, order: order }, function() { currentlyExecutingSuites.push(topSuite); processor.execute(function() { clearResourcesForRunnable(topSuite.id); currentlyExecutingSuites.pop(); var overallStatus, incompleteReason; if (hasFailures || topSuite.result.failedExpectations.length > 0) { overallStatus = 'failed'; } else if (focusedRunnables.length > 0) { overallStatus = 'incomplete'; incompleteReason = 'fit() or fdescribe() was found'; } else if (totalSpecsDefined === 0) { overallStatus = 'incomplete'; incompleteReason = 'No specs found'; } else { overallStatus = 'passed'; } /** * Information passed to the {@link Reporter#jasmineDone} event. * @typedef JasmineDoneInfo * @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'. * @property {Int} totalTime - The total time (in ms) that it took to execute the suite * @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete. * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. * @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level. * @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level. */ reporter.jasmineDone( { overallStatus: overallStatus, totalTime: jasmineTimer.elapsed(), incompleteReason: incompleteReason, order: order, failedExpectations: topSuite.result.failedExpectations, deprecationWarnings: topSuite.result.deprecationWarnings }, function() {} ); }); } ); }; /** * Add a custom reporter to the Jasmine environment. * @name Env#addReporter * @since 2.0.0 * @function * @param {Reporter} reporterToAdd The reporter to be added. * @see custom_reporter */ this.addReporter = function(reporterToAdd) { reporter.addReporter(reporterToAdd); }; /** * Provide a fallback reporter if no other reporters have been specified. * @name Env#provideFallbackReporter * @since 2.5.0 * @function * @param {Reporter} reporterToAdd The reporter * @see custom_reporter */ this.provideFallbackReporter = function(reporterToAdd) { reporter.provideFallbackReporter(reporterToAdd); }; /** * Clear all registered reporters * @name Env#clearReporters * @since 2.5.2 * @function */ this.clearReporters = function() { reporter.clearReporters(); }; var spyFactory = new j$.SpyFactory( function getCustomStrategies() { var runnable = currentRunnable(); if (runnable) { return runnableResources[runnable.id].customSpyStrategies; } return {}; }, function getDefaultStrategyFn() { var runnable = currentRunnable(); if (runnable) { return runnableResources[runnable.id].defaultStrategyFn; } return undefined; }, function getPromise() { return customPromise || global.Promise; } ); var spyRegistry = new j$.SpyRegistry({ currentSpies: function() { if (!currentRunnable()) { throw new Error( 'Spies must be created in a before function or a spec' ); } return runnableResources[currentRunnable().id].spies; }, createSpy: function(name, originalFn) { return self.createSpy(name, originalFn); } }); this.allowRespy = function(allow) { spyRegistry.allowRespy(allow); }; this.spyOn = function() { return spyRegistry.spyOn.apply(spyRegistry, arguments); }; this.spyOnProperty = function() { return spyRegistry.spyOnProperty.apply(spyRegistry, arguments); }; this.spyOnAllFunctions = function() { return spyRegistry.spyOnAllFunctions.apply(spyRegistry, arguments); }; this.createSpy = function(name, originalFn) { if (arguments.length === 1 && j$.isFunction_(name)) { originalFn = name; name = originalFn.name; } return spyFactory.createSpy(name, originalFn); }; this.createSpyObj = function(baseName, methodNames, propertyNames) { return spyFactory.createSpyObj(baseName, methodNames, propertyNames); }; var ensureIsFunction = function(fn, caller) { if (!j$.isFunction_(fn)) { throw new Error( caller + ' expects a function argument; received ' + j$.getType_(fn) ); } }; var ensureIsFunctionOrAsync = function(fn, caller) { if (!j$.isFunction_(fn) && !j$.isAsyncFunction_(fn)) { throw new Error( caller + ' expects a function argument; received ' + j$.getType_(fn) ); } }; function ensureIsNotNested(method) { var runnable = currentRunnable(); if (runnable !== null && runnable !== undefined) { throw new Error( "'" + method + "' should only be used in 'describe' function" ); } } var suiteFactory = function(description) { var suite = new j$.Suite({ env: self, id: getNextSuiteId(), description: description, parentSuite: currentDeclarationSuite, timer: new j$.Timer(), expectationFactory: expectationFactory, asyncExpectationFactory: suiteAsyncExpectationFactory, expectationResultFactory: expectationResultFactory, throwOnExpectationFailure: config.oneFailurePerSpec }); return suite; }; this.describe = function(description, specDefinitions) { ensureIsNotNested('describe'); ensureIsFunction(specDefinitions, 'describe'); var suite = suiteFactory(description); if (specDefinitions.length > 0) { throw new Error('describe does not expect any arguments'); } if (currentDeclarationSuite.markedPending) { suite.pend(); } addSpecsToSuite(suite, specDefinitions); return suite; }; this.xdescribe = function(description, specDefinitions) { ensureIsNotNested('xdescribe'); ensureIsFunction(specDefinitions, 'xdescribe'); var suite = suiteFactory(description); suite.pend(); addSpecsToSuite(suite, specDefinitions); return suite; }; var focusedRunnables = []; this.fdescribe = function(description, specDefinitions) { ensureIsNotNested('fdescribe'); ensureIsFunction(specDefinitions, 'fdescribe'); var suite = suiteFactory(description); suite.isFocused = true; focusedRunnables.push(suite.id); unfocusAncestor(); addSpecsToSuite(suite, specDefinitions); return suite; }; function addSpecsToSuite(suite, specDefinitions) { var parentSuite = currentDeclarationSuite; parentSuite.addChild(suite); currentDeclarationSuite = suite; var declarationError = null; try { specDefinitions.call(suite); } catch (e) { declarationError = e; } if (declarationError) { suite.onException(declarationError); } currentDeclarationSuite = parentSuite; } function findFocusedAncestor(suite) { while (suite) { if (suite.isFocused) { return suite.id; } suite = suite.parentSuite; } return null; } function unfocusAncestor() { var focusedAncestor = findFocusedAncestor(currentDeclarationSuite); if (focusedAncestor) { for (var i = 0; i < focusedRunnables.length; i++) { if (focusedRunnables[i] === focusedAncestor) { focusedRunnables.splice(i, 1); break; } } } } var specFactory = function(description, fn, suite, timeout) { totalSpecsDefined++; var spec = new j$.Spec({ id: getNextSpecId(), beforeAndAfterFns: beforeAndAfterFns(suite), expectationFactory: expectationFactory, asyncExpectationFactory: specAsyncExpectationFactory, resultCallback: specResultCallback, getSpecName: function(spec) { return getSpecName(spec, suite); }, onStart: specStarted, description: description, expectationResultFactory: expectationResultFactory, queueRunnerFactory: queueRunnerFactory, userContext: function() { return suite.clonedSharedUserContext(); }, queueableFn: { fn: fn, timeout: timeout || 0 }, throwOnExpectationFailure: config.oneFailurePerSpec, timer: new j$.Timer() }); return spec; function specResultCallback(result, next) { clearResourcesForRunnable(spec.id); currentSpec = null; if (result.status === 'failed') { hasFailures = true; } reporter.specDone(result, next); } function specStarted(spec, next) { currentSpec = spec; defaultResourcesForRunnable(spec.id, suite.id); reporter.specStarted(spec.result, next); } }; this.it = function(description, fn, timeout) { ensureIsNotNested('it'); // it() sometimes doesn't have a fn argument, so only check the type if // it's given. if (arguments.length > 1 && typeof fn !== 'undefined') { ensureIsFunctionOrAsync(fn, 'it'); } var spec = specFactory(description, fn, currentDeclarationSuite, timeout); if (currentDeclarationSuite.markedPending) { spec.pend(); } currentDeclarationSuite.addChild(spec); return spec; }; this.xit = function(description, fn, timeout) { ensureIsNotNested('xit'); // xit(), like it(), doesn't always have a fn argument, so only check the // type when needed. if (arguments.length > 1 && typeof fn !== 'undefined') { ensureIsFunctionOrAsync(fn, 'xit'); } var spec = this.it.apply(this, arguments); spec.pend('Temporarily disabled with xit'); return spec; }; this.fit = function(description, fn, timeout) { ensureIsNotNested('fit'); ensureIsFunctionOrAsync(fn, 'fit'); var spec = specFactory(description, fn, currentDeclarationSuite, timeout); currentDeclarationSuite.addChild(spec); focusedRunnables.push(spec.id); unfocusAncestor(); return spec; }; /** * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult} * @name Env#setSpecProperty * @since 3.6.0 * @function * @param {String} key The name of the property * @param {*} value The value of the property */ this.setSpecProperty = function(key, value) { if (!currentRunnable() || currentRunnable() == currentSuite()) { throw new Error( "'setSpecProperty' was used when there was no current spec" ); } currentRunnable().setSpecProperty(key, value); }; /** * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult} * @name Env#setSuiteProperty * @since 3.6.0 * @function * @param {String} key The name of the property * @param {*} value The value of the property */ this.setSuiteProperty = function(key, value) { if (!currentSuite()) { throw new Error( "'setSuiteProperty' was used when there was no current suite" ); } currentSuite().setSuiteProperty(key, value); }; this.expect = function(actual) { if (!currentRunnable()) { throw new Error( "'expect' was used when there was no current spec, this could be because an asynchronous test timed out" ); } return currentRunnable().expect(actual); }; this.expectAsync = function(actual) { if (!currentRunnable()) { throw new Error( "'expectAsync' was used when there was no current spec, this could be because an asynchronous test timed out" ); } return currentRunnable().expectAsync(actual); }; this.beforeEach = function(beforeEachFunction, timeout) { ensureIsNotNested('beforeEach'); ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach'); currentDeclarationSuite.beforeEach({ fn: beforeEachFunction, timeout: timeout || 0 }); }; this.beforeAll = function(beforeAllFunction, timeout) { ensureIsNotNested('beforeAll'); ensureIsFunctionOrAsync(beforeAllFunction, 'beforeAll'); currentDeclarationSuite.beforeAll({ fn: beforeAllFunction, timeout: timeout || 0 }); }; this.afterEach = function(afterEachFunction, timeout) { ensureIsNotNested('afterEach'); ensureIsFunctionOrAsync(afterEachFunction, 'afterEach'); afterEachFunction.isCleanup = true; currentDeclarationSuite.afterEach({ fn: afterEachFunction, timeout: timeout || 0 }); }; this.afterAll = function(afterAllFunction, timeout) { ensureIsNotNested('afterAll'); ensureIsFunctionOrAsync(afterAllFunction, 'afterAll'); currentDeclarationSuite.afterAll({ fn: afterAllFunction, timeout: timeout || 0 }); }; this.pending = function(message) { var fullMessage = j$.Spec.pendingSpecExceptionMessage; if (message) { fullMessage += message; } throw fullMessage; }; this.fail = function(error) { if (!currentRunnable()) { throw new Error( "'fail' was used when there was no current spec, this could be because an asynchronous test timed out" ); } var message = 'Failed'; if (error) { message += ': '; if (error.message) { message += error.message; } else if (j$.isString_(error)) { message += error; } else { // pretty print all kind of objects. This includes arrays. message += makePrettyPrinter()(error); } } currentRunnable().addExpectationResult(false, { matcherName: '', passed: false, expected: '', actual: '', message: message, error: error && error.message ? error : null }); if (config.oneFailurePerSpec) { throw new Error(message); } }; this.cleanup_ = function() { if (globalErrors) { globalErrors.uninstall(); } }; } return Env; }; getJasmineRequireObj().JsApiReporter = function(j$) { /** * @name jsApiReporter * @classdesc {@link Reporter} added by default in `boot.js` to record results for retrieval in javascript code. An instance is made available as `jsApiReporter` on the global object. * @class * @hideconstructor */ function JsApiReporter(options) { var timer = options.timer || new j$.Timer(), status = 'loaded'; this.started = false; this.finished = false; this.runDetails = {}; this.jasmineStarted = function() { this.started = true; status = 'started'; timer.start(); }; var executionTime; this.jasmineDone = function(runDetails) { this.finished = true; this.runDetails = runDetails; executionTime = timer.elapsed(); status = 'done'; }; /** * Get the current status for the Jasmine environment. * @name jsApiReporter#status * @since 2.0.0 * @function * @return {String} - One of `loaded`, `started`, or `done` */ this.status = function() { return status; }; var suites = [], suites_hash = {}; this.suiteStarted = function(result) { suites_hash[result.id] = result; }; this.suiteDone = function(result) { storeSuite(result); }; /** * Get the results for a set of suites. * * Retrievable in slices for easier serialization. * @name jsApiReporter#suiteResults * @since 2.1.0 * @function * @param {Number} index - The position in the suites list to start from. * @param {Number} length - Maximum number of suite results to return. * @return {SuiteResult[]} */ this.suiteResults = function(index, length) { return suites.slice(index, index + length); }; function storeSuite(result) { suites.push(result); suites_hash[result.id] = result; } /** * Get all of the suites in a single object, with their `id` as the key. * @name jsApiReporter#suites * @since 2.0.0 * @function * @return {Object} - Map of suite id to {@link SuiteResult} */ this.suites = function() { return suites_hash; }; var specs = []; this.specDone = function(result) { specs.push(result); }; /** * Get the results for a set of specs. * * Retrievable in slices for easier serialization. * @name jsApiReporter#specResults * @since 2.0.0 * @function * @param {Number} index - The position in the specs list to start from. * @param {Number} length - Maximum number of specs results to return. * @return {SpecResult[]} */ this.specResults = function(index, length) { return specs.slice(index, index + length); }; /** * Get all spec results. * @name jsApiReporter#specs * @since 2.0.0 * @function * @return {SpecResult[]} */ this.specs = function() { return specs; }; /** * Get the number of milliseconds it took for the full Jasmine suite to run. * @name jsApiReporter#executionTime * @since 2.0.0 * @function * @return {Number} */ this.executionTime = function() { return executionTime; }; } return JsApiReporter; }; getJasmineRequireObj().Any = function(j$) { function Any(expectedObject) { if (typeof expectedObject === 'undefined') { throw new TypeError( 'jasmine.any() expects to be passed a constructor function. ' + 'Please pass one or use jasmine.anything() to match any object.' ); } this.expectedObject = expectedObject; } Any.prototype.asymmetricMatch = function(other) { if (this.expectedObject == String) { return typeof other == 'string' || other instanceof String; } if (this.expectedObject == Number) { return typeof other == 'number' || other instanceof Number; } if (this.expectedObject == Function) { return typeof other == 'function' || other instanceof Function; } if (this.expectedObject == Object) { return other !== null && typeof other == 'object'; } if (this.expectedObject == Boolean) { return typeof other == 'boolean'; } /* jshint -W122 */ /* global Symbol */ if (typeof Symbol != 'undefined' && this.expectedObject == Symbol) { return typeof other == 'symbol'; } /* jshint +W122 */ return other instanceof this.expectedObject; }; Any.prototype.jasmineToString = function() { return ''; }; return Any; }; getJasmineRequireObj().Anything = function(j$) { function Anything() {} Anything.prototype.asymmetricMatch = function(other) { return !j$.util.isUndefined(other) && other !== null; }; Anything.prototype.jasmineToString = function() { return ''; }; return Anything; }; getJasmineRequireObj().ArrayContaining = function(j$) { function ArrayContaining(sample) { this.sample = sample; } ArrayContaining.prototype.asymmetricMatch = function(other, matchersUtil) { if (!j$.isArray_(this.sample)) { throw new Error('You must provide an array to arrayContaining, not ' + j$.pp(this.sample) + '.'); } // If the actual parameter is not an array, we can fail immediately, since it couldn't // possibly be an "array containing" anything. However, we also want an empty sample // array to match anything, so we need to double-check we aren't in that case if (!j$.isArray_(other) && this.sample.length > 0) { return false; } for (var i = 0; i < this.sample.length; i++) { var item = this.sample[i]; if (!matchersUtil.contains(other, item)) { return false; } } return true; }; ArrayContaining.prototype.jasmineToString = function (pp) { return ''; }; return ArrayContaining; }; getJasmineRequireObj().ArrayWithExactContents = function(j$) { function ArrayWithExactContents(sample) { this.sample = sample; } ArrayWithExactContents.prototype.asymmetricMatch = function(other, matchersUtil) { if (!j$.isArray_(this.sample)) { throw new Error('You must provide an array to arrayWithExactContents, not ' + j$.pp(this.sample) + '.'); } if (this.sample.length !== other.length) { return false; } for (var i = 0; i < this.sample.length; i++) { var item = this.sample[i]; if (!matchersUtil.contains(other, item)) { return false; } } return true; }; ArrayWithExactContents.prototype.jasmineToString = function(pp) { return ''; }; return ArrayWithExactContents; }; getJasmineRequireObj().Empty = function (j$) { function Empty() {} Empty.prototype.asymmetricMatch = function (other) { if (j$.isString_(other) || j$.isArray_(other) || j$.isTypedArray_(other)) { return other.length === 0; } if (j$.isMap(other) || j$.isSet(other)) { return other.size === 0; } if (j$.isObject_(other)) { return Object.keys(other).length === 0; } return false; }; Empty.prototype.jasmineToString = function () { return ''; }; return Empty; }; getJasmineRequireObj().Falsy = function(j$) { function Falsy() {} Falsy.prototype.asymmetricMatch = function(other) { return !other; }; Falsy.prototype.jasmineToString = function() { return ''; }; return Falsy; }; getJasmineRequireObj().MapContaining = function(j$) { function MapContaining(sample) { if (!j$.isMap(sample)) { throw new Error('You must provide a map to `mapContaining`, not ' + j$.pp(sample)); } this.sample = sample; } MapContaining.prototype.asymmetricMatch = function(other, matchersUtil) { if (!j$.isMap(other)) return false; var hasAllMatches = true; j$.util.forEachBreakable(this.sample, function(breakLoop, value, key) { // for each key/value pair in `sample` // there should be at least one pair in `other` whose key and value both match var hasMatch = false; j$.util.forEachBreakable(other, function(oBreakLoop, oValue, oKey) { if ( matchersUtil.equals(oKey, key) && matchersUtil.equals(oValue, value) ) { hasMatch = true; oBreakLoop(); } }); if (!hasMatch) { hasAllMatches = false; breakLoop(); } }); return hasAllMatches; }; MapContaining.prototype.jasmineToString = function(pp) { return ''; }; return MapContaining; }; getJasmineRequireObj().NotEmpty = function (j$) { function NotEmpty() {} NotEmpty.prototype.asymmetricMatch = function (other) { if (j$.isString_(other) || j$.isArray_(other) || j$.isTypedArray_(other)) { return other.length !== 0; } if (j$.isMap(other) || j$.isSet(other)) { return other.size !== 0; } if (j$.isObject_(other)) { return Object.keys(other).length !== 0; } return false; }; NotEmpty.prototype.jasmineToString = function () { return ''; }; return NotEmpty; }; getJasmineRequireObj().ObjectContaining = function(j$) { function ObjectContaining(sample) { this.sample = sample; } function getPrototype(obj) { if (Object.getPrototypeOf) { return Object.getPrototypeOf(obj); } if (obj.constructor.prototype == obj) { return null; } return obj.constructor.prototype; } function hasProperty(obj, property) { if (!obj || typeof(obj) !== 'object') { return false; } if (Object.prototype.hasOwnProperty.call(obj, property)) { return true; } return hasProperty(getPrototype(obj), property); } ObjectContaining.prototype.asymmetricMatch = function(other, matchersUtil) { if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); } if (typeof(other) !== 'object') { return false; } for (var property in this.sample) { if (!hasProperty(other, property) || !matchersUtil.equals(this.sample[property], other[property])) { return false; } } return true; }; ObjectContaining.prototype.valuesForDiff_ = function(other, pp) { if (!j$.isObject_(other)) { return { self: this.jasmineToString(pp), other: other }; } var filteredOther = {}; Object.keys(this.sample).forEach(function (k) { // eq short-circuits comparison of objects that have different key sets, // so include all keys even if undefined. filteredOther[k] = other[k]; }); return { self: this.sample, other: filteredOther }; }; ObjectContaining.prototype.jasmineToString = function(pp) { return ''; }; return ObjectContaining; }; getJasmineRequireObj().SetContaining = function(j$) { function SetContaining(sample) { if (!j$.isSet(sample)) { throw new Error('You must provide a set to `setContaining`, not ' + j$.pp(sample)); } this.sample = sample; } SetContaining.prototype.asymmetricMatch = function(other, matchersUtil) { if (!j$.isSet(other)) return false; var hasAllMatches = true; j$.util.forEachBreakable(this.sample, function(breakLoop, item) { // for each item in `sample` there should be at least one matching item in `other` // (not using `matchersUtil.contains` because it compares set members by reference, // not by deep value equality) var hasMatch = false; j$.util.forEachBreakable(other, function(oBreakLoop, oItem) { if (matchersUtil.equals(oItem, item)) { hasMatch = true; oBreakLoop(); } }); if (!hasMatch) { hasAllMatches = false; breakLoop(); } }); return hasAllMatches; }; SetContaining.prototype.jasmineToString = function(pp) { return ''; }; return SetContaining; }; getJasmineRequireObj().StringMatching = function(j$) { function StringMatching(expected) { if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { throw new Error('Expected is not a String or a RegExp'); } this.regexp = new RegExp(expected); } StringMatching.prototype.asymmetricMatch = function(other) { return this.regexp.test(other); }; StringMatching.prototype.jasmineToString = function() { return ''; }; return StringMatching; }; getJasmineRequireObj().Truthy = function(j$) { function Truthy() {} Truthy.prototype.asymmetricMatch = function(other) { return !!other; }; Truthy.prototype.jasmineToString = function() { return ''; }; return Truthy; }; getJasmineRequireObj().asymmetricEqualityTesterArgCompatShim = function(j$) { /* Older versions of Jasmine passed an array of custom equality testers as the second argument to each asymmetric equality tester's `asymmetricMatch` method. Newer versions will pass a `MatchersUtil` instance. The asymmetricEqualityTesterArgCompatShim allows for a graceful migration from the old interface to the new by "being" both an array of custom equality testers and a `MatchersUtil` at the same time. This code should be removed in the next major release. */ var likelyArrayProps = [ 'concat', 'constructor', 'copyWithin', 'entries', 'every', 'fill', 'filter', 'find', 'findIndex', 'flat', 'flatMap', 'forEach', 'includes', 'indexOf', 'join', 'keys', 'lastIndexOf', 'length', 'map', 'pop', 'push', 'reduce', 'reduceRight', 'reverse', 'shift', 'slice', 'some', 'sort', 'splice', 'toLocaleString', 'toSource', 'toString', 'unshift', 'values' ]; function asymmetricEqualityTesterArgCompatShim( matchersUtil, customEqualityTesters ) { var self = Object.create(matchersUtil), props, i, k; copy(self, customEqualityTesters, 'length'); for (i = 0; i < customEqualityTesters.length; i++) { copy(self, customEqualityTesters, i); } var props = arrayProps(); for (i = 0; i < props.length; i++) { k = props[i]; if (k !== 'length') { copy(self, Array.prototype, k); } } return self; } function copy(dest, src, propName) { Object.defineProperty(dest, propName, { get: function() { return src[propName]; } }); } function arrayProps() { var props, a, k; if (!Object.getOwnPropertyDescriptors) { return likelyArrayProps.filter(function(k) { return Array.prototype.hasOwnProperty(k); }); } props = Object.getOwnPropertyDescriptors(Array.prototype); // eslint-disable-line compat/compat a = []; for (k in props) { a.push(k); } return a; } return asymmetricEqualityTesterArgCompatShim; }; getJasmineRequireObj().CallTracker = function(j$) { /** * @namespace Spy#calls * @since 2.0.0 */ function CallTracker() { var calls = []; var opts = {}; this.track = function(context) { if (opts.cloneArgs) { context.args = j$.util.cloneArgs(context.args); } calls.push(context); }; /** * Check whether this spy has been invoked. * @name Spy#calls#any * @since 2.0.0 * @function * @return {Boolean} */ this.any = function() { return !!calls.length; }; /** * Get the number of invocations of this spy. * @name Spy#calls#count * @since 2.0.0 * @function * @return {Integer} */ this.count = function() { return calls.length; }; /** * Get the arguments that were passed to a specific invocation of this spy. * @name Spy#calls#argsFor * @since 2.0.0 * @function * @param {Integer} index The 0-based invocation index. * @return {Array} */ this.argsFor = function(index) { var call = calls[index]; return call ? call.args : []; }; /** * Get the raw calls array for this spy. * @name Spy#calls#all * @since 2.0.0 * @function * @return {Spy.callData[]} */ this.all = function() { return calls; }; /** * Get all of the arguments for each invocation of this spy in the order they were received. * @name Spy#calls#allArgs * @since 2.0.0 * @function * @return {Array} */ this.allArgs = function() { var callArgs = []; for (var i = 0; i < calls.length; i++) { callArgs.push(calls[i].args); } return callArgs; }; /** * Get the first invocation of this spy. * @name Spy#calls#first * @since 2.0.0 * @function * @return {ObjecSpy.callData} */ this.first = function() { return calls[0]; }; /** * Get the most recent invocation of this spy. * @name Spy#calls#mostRecent * @since 2.0.0 * @function * @return {ObjecSpy.callData} */ this.mostRecent = function() { return calls[calls.length - 1]; }; /** * Reset this spy as if it has never been called. * @name Spy#calls#reset * @since 2.0.0 * @function */ this.reset = function() { calls = []; }; /** * Set this spy to do a shallow clone of arguments passed to each invocation. * @name Spy#calls#saveArgumentsByValue * @since 2.5.0 * @function */ this.saveArgumentsByValue = function() { opts.cloneArgs = true; }; } return CallTracker; }; getJasmineRequireObj().clearStack = function(j$) { var maxInlineCallCount = 10; function messageChannelImpl(global, setTimeout) { var channel = new global.MessageChannel(), head = {}, tail = head; var taskRunning = false; channel.port1.onmessage = function() { head = head.next; var task = head.task; delete head.task; if (taskRunning) { global.setTimeout(task, 0); } else { try { taskRunning = true; task(); } finally { taskRunning = false; } } }; var currentCallCount = 0; return function clearStack(fn) { currentCallCount++; if (currentCallCount < maxInlineCallCount) { tail = tail.next = { task: fn }; channel.port2.postMessage(0); } else { currentCallCount = 0; setTimeout(fn); } }; } function getClearStack(global) { var currentCallCount = 0; var realSetTimeout = global.setTimeout; var setTimeoutImpl = function clearStack(fn) { Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]); }; if (global.setImmediate && j$.isFunction_(global.setImmediate)) { var realSetImmediate = global.setImmediate; return function(fn) { currentCallCount++; if (currentCallCount < maxInlineCallCount) { realSetImmediate(fn); } else { currentCallCount = 0; setTimeoutImpl(fn); } }; } else if (global.MessageChannel && !j$.util.isUndefined(global.MessageChannel)) { return messageChannelImpl(global, setTimeoutImpl); } else { return setTimeoutImpl; } } return getClearStack; }; getJasmineRequireObj().Clock = function() { /* global process */ var NODE_JS = typeof process !== 'undefined' && process.versions && typeof process.versions.node === 'string'; /** * _Note:_ Do not construct this directly, Jasmine will make one during booting. You can get the current clock with {@link jasmine.clock}. * @class Clock * @classdesc Jasmine's mock clock is used when testing time dependent code. */ function Clock(global, delayedFunctionSchedulerFactory, mockDate) { var self = this, realTimingFunctions = { setTimeout: global.setTimeout, clearTimeout: global.clearTimeout, setInterval: global.setInterval, clearInterval: global.clearInterval }, fakeTimingFunctions = { setTimeout: setTimeout, clearTimeout: clearTimeout, setInterval: setInterval, clearInterval: clearInterval }, installed = false, delayedFunctionScheduler, timer; self.FakeTimeout = FakeTimeout; /** * Install the mock clock over the built-in methods. * @name Clock#install * @since 2.0.0 * @function * @return {Clock} */ self.install = function() { if (!originalTimingFunctionsIntact()) { throw new Error( 'Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?' ); } replace(global, fakeTimingFunctions); timer = fakeTimingFunctions; delayedFunctionScheduler = delayedFunctionSchedulerFactory(); installed = true; return self; }; /** * Uninstall the mock clock, returning the built-in methods to their places. * @name Clock#uninstall * @since 2.0.0 * @function */ self.uninstall = function() { delayedFunctionScheduler = null; mockDate.uninstall(); replace(global, realTimingFunctions); timer = realTimingFunctions; installed = false; }; /** * Execute a function with a mocked Clock * * The clock will be {@link Clock#install|install}ed before the function is called and {@link Clock#uninstall|uninstall}ed in a `finally` after the function completes. * @name Clock#withMock * @since 2.3.0 * @function * @param {Function} closure The function to be called. */ self.withMock = function(closure) { this.install(); try { closure(); } finally { this.uninstall(); } }; /** * Instruct the installed Clock to also mock the date returned by `new Date()` * @name Clock#mockDate * @since 2.1.0 * @function * @param {Date} [initialDate=now] The `Date` to provide. */ self.mockDate = function(initialDate) { mockDate.install(initialDate); }; self.setTimeout = function(fn, delay, params) { return Function.prototype.apply.apply(timer.setTimeout, [ global, arguments ]); }; self.setInterval = function(fn, delay, params) { return Function.prototype.apply.apply(timer.setInterval, [ global, arguments ]); }; self.clearTimeout = function(id) { return Function.prototype.call.apply(timer.clearTimeout, [global, id]); }; self.clearInterval = function(id) { return Function.prototype.call.apply(timer.clearInterval, [global, id]); }; /** * Tick the Clock forward, running any enqueued timeouts along the way * @name Clock#tick * @since 1.3.0 * @function * @param {int} millis The number of milliseconds to tick. */ self.tick = function(millis) { if (installed) { delayedFunctionScheduler.tick(millis, function(millis) { mockDate.tick(millis); }); } else { throw new Error( 'Mock clock is not installed, use jasmine.clock().install()' ); } }; return self; function originalTimingFunctionsIntact() { return ( global.setTimeout === realTimingFunctions.setTimeout && global.clearTimeout === realTimingFunctions.clearTimeout && global.setInterval === realTimingFunctions.setInterval && global.clearInterval === realTimingFunctions.clearInterval ); } function replace(dest, source) { for (var prop in source) { dest[prop] = source[prop]; } } function setTimeout(fn, delay) { if (!NODE_JS) { return delayedFunctionScheduler.scheduleFunction( fn, delay, argSlice(arguments, 2) ); } var timeout = new FakeTimeout(); delayedFunctionScheduler.scheduleFunction( fn, delay, argSlice(arguments, 2), false, timeout ); return timeout; } function clearTimeout(id) { return delayedFunctionScheduler.removeFunctionWithId(id); } function setInterval(fn, interval) { if (!NODE_JS) { return delayedFunctionScheduler.scheduleFunction( fn, interval, argSlice(arguments, 2), true ); } var timeout = new FakeTimeout(); delayedFunctionScheduler.scheduleFunction( fn, interval, argSlice(arguments, 2), true, timeout ); return timeout; } function clearInterval(id) { return delayedFunctionScheduler.removeFunctionWithId(id); } function argSlice(argsObj, n) { return Array.prototype.slice.call(argsObj, n); } } /** * Mocks Node.js Timeout class */ function FakeTimeout() {} FakeTimeout.prototype.ref = function() { return this; }; FakeTimeout.prototype.unref = function() { return this; }; return Clock; }; getJasmineRequireObj().DelayedFunctionScheduler = function(j$) { function DelayedFunctionScheduler() { var self = this; var scheduledLookup = []; var scheduledFunctions = {}; var currentTime = 0; var delayedFnCount = 0; var deletedKeys = []; self.tick = function(millis, tickDate) { millis = millis || 0; var endTime = currentTime + millis; runScheduledFunctions(endTime, tickDate); currentTime = endTime; }; self.scheduleFunction = function( funcToCall, millis, params, recurring, timeoutKey, runAtMillis ) { var f; if (typeof funcToCall === 'string') { /* jshint evil: true */ f = function() { return eval(funcToCall); }; /* jshint evil: false */ } else { f = funcToCall; } millis = millis || 0; timeoutKey = timeoutKey || ++delayedFnCount; runAtMillis = runAtMillis || currentTime + millis; var funcToSchedule = { runAtMillis: runAtMillis, funcToCall: f, recurring: recurring, params: params, timeoutKey: timeoutKey, millis: millis }; if (runAtMillis in scheduledFunctions) { scheduledFunctions[runAtMillis].push(funcToSchedule); } else { scheduledFunctions[runAtMillis] = [funcToSchedule]; scheduledLookup.push(runAtMillis); scheduledLookup.sort(function(a, b) { return a - b; }); } return timeoutKey; }; self.removeFunctionWithId = function(timeoutKey) { deletedKeys.push(timeoutKey); for (var runAtMillis in scheduledFunctions) { var funcs = scheduledFunctions[runAtMillis]; var i = indexOfFirstToPass(funcs, function(func) { return func.timeoutKey === timeoutKey; }); if (i > -1) { if (funcs.length === 1) { delete scheduledFunctions[runAtMillis]; deleteFromLookup(runAtMillis); } else { funcs.splice(i, 1); } // intervals get rescheduled when executed, so there's never more // than a single scheduled function with a given timeoutKey break; } } }; return self; function indexOfFirstToPass(array, testFn) { var index = -1; for (var i = 0; i < array.length; ++i) { if (testFn(array[i])) { index = i; break; } } return index; } function deleteFromLookup(key) { var value = Number(key); var i = indexOfFirstToPass(scheduledLookup, function(millis) { return millis === value; }); if (i > -1) { scheduledLookup.splice(i, 1); } } function reschedule(scheduledFn) { self.scheduleFunction( scheduledFn.funcToCall, scheduledFn.millis, scheduledFn.params, true, scheduledFn.timeoutKey, scheduledFn.runAtMillis + scheduledFn.millis ); } function forEachFunction(funcsToRun, callback) { for (var i = 0; i < funcsToRun.length; ++i) { callback(funcsToRun[i]); } } function runScheduledFunctions(endTime, tickDate) { tickDate = tickDate || function() {}; if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) { tickDate(endTime - currentTime); return; } do { deletedKeys = []; var newCurrentTime = scheduledLookup.shift(); tickDate(newCurrentTime - currentTime); currentTime = newCurrentTime; var funcsToRun = scheduledFunctions[currentTime]; delete scheduledFunctions[currentTime]; forEachFunction(funcsToRun, function(funcToRun) { if (funcToRun.recurring) { reschedule(funcToRun); } }); forEachFunction(funcsToRun, function(funcToRun) { if (j$.util.arrayContains(deletedKeys, funcToRun.timeoutKey)) { // skip a timeoutKey deleted whilst we were running return; } funcToRun.funcToCall.apply(null, funcToRun.params || []); }); deletedKeys = []; } while ( scheduledLookup.length > 0 && // checking first if we're out of time prevents setTimeout(0) // scheduled in a funcToRun from forcing an extra iteration currentTime !== endTime && scheduledLookup[0] <= endTime ); // ran out of functions to call, but still time left on the clock if (currentTime !== endTime) { tickDate(endTime - currentTime); } } } return DelayedFunctionScheduler; }; getJasmineRequireObj().errors = function() { function ExpectationFailed() {} ExpectationFailed.prototype = new Error(); ExpectationFailed.prototype.constructor = ExpectationFailed; return { ExpectationFailed: ExpectationFailed }; }; getJasmineRequireObj().ExceptionFormatter = function(j$) { var ignoredProperties = [ 'name', 'message', 'stack', 'fileName', 'sourceURL', 'line', 'lineNumber', 'column', 'description', 'jasmineMessage' ]; function ExceptionFormatter(options) { var jasmineFile = (options && options.jasmineFile) || j$.util.jasmineFile(); this.message = function(error) { var message = ''; if (error.jasmineMessage) { message += error.jasmineMessage; } else if (error.name && error.message) { message += error.name + ': ' + error.message; } else if (error.message) { message += error.message; } else { message += error.toString() + ' thrown'; } if (error.fileName || error.sourceURL) { message += ' in ' + (error.fileName || error.sourceURL); } if (error.line || error.lineNumber) { message += ' (line ' + (error.line || error.lineNumber) + ')'; } return message; }; this.stack = function(error) { if (!error || !error.stack) { return null; } var stackTrace = new j$.StackTrace(error); var lines = filterJasmine(stackTrace); var result = ''; if (stackTrace.message) { lines.unshift(stackTrace.message); } result += formatProperties(error); result += lines.join('\n'); return result; }; function filterJasmine(stackTrace) { var result = [], jasmineMarker = stackTrace.style === 'webkit' ? '' : ' at '; stackTrace.frames.forEach(function(frame) { if (frame.file && frame.file !== jasmineFile) { result.push(frame.raw); } else if (result.length && result[result.length - 1] !== jasmineMarker) { result.push(jasmineMarker); } }); return result; } function formatProperties(error) { if (!(error instanceof Object)) { return; } var result = {}; var empty = true; for (var prop in error) { if (j$.util.arrayContains(ignoredProperties, prop)) { continue; } result[prop] = error[prop]; empty = false; } if (!empty) { return 'error properties: ' + j$.pp(result) + '\n'; } return ''; } } return ExceptionFormatter; }; getJasmineRequireObj().Expectation = function(j$) { /** * Matchers that come with Jasmine out of the box. * @namespace matchers */ function Expectation(options) { this.expector = new j$.Expector(options); var customMatchers = options.customMatchers || {}; for (var matcherName in customMatchers) { this[matcherName] = wrapSyncCompare( matcherName, customMatchers[matcherName] ); } } /** * Add some context for an {@link expect} * @function * @name matchers#withContext * @since 3.3.0 * @param {String} message - Additional context to show when the matcher fails * @return {matchers} */ Expectation.prototype.withContext = function withContext(message) { return addFilter(this, new ContextAddingFilter(message)); }; /** * Invert the matcher following this {@link expect} * @member * @name matchers#not * @since 1.3.0 * @type {matchers} * @example * expect(something).not.toBe(true); */ Object.defineProperty(Expectation.prototype, 'not', { get: function() { return addFilter(this, syncNegatingFilter); } }); /** * Asynchronous matchers. * @namespace async-matchers */ function AsyncExpectation(options) { var global = options.global || j$.getGlobal(); this.expector = new j$.Expector(options); if (!global.Promise) { throw new Error( 'expectAsync is unavailable because the environment does not support promises.' ); } var customAsyncMatchers = options.customAsyncMatchers || {}; for (var matcherName in customAsyncMatchers) { this[matcherName] = wrapAsyncCompare( matcherName, customAsyncMatchers[matcherName] ); } } /** * Add some context for an {@link expectAsync} * @function * @name async-matchers#withContext * @since 3.3.0 * @param {String} message - Additional context to show when the async matcher fails * @return {async-matchers} */ AsyncExpectation.prototype.withContext = function withContext(message) { return addFilter(this, new ContextAddingFilter(message)); }; /** * Invert the matcher following this {@link expectAsync} * @member * @name async-matchers#not * @type {async-matchers} * @example * await expectAsync(myPromise).not.toBeResolved(); * @example * return expectAsync(myPromise).not.toBeResolved(); */ Object.defineProperty(AsyncExpectation.prototype, 'not', { get: function() { return addFilter(this, asyncNegatingFilter); } }); function wrapSyncCompare(name, matcherFactory) { return function() { var result = this.expector.compare(name, matcherFactory, arguments); this.expector.processResult(result); }; } function wrapAsyncCompare(name, matcherFactory) { return function() { var self = this; // Capture the call stack here, before we go async, so that it will contain // frames that are relevant to the user instead of just parts of Jasmine. var errorForStack = j$.util.errorWithStack(); return this.expector .compare(name, matcherFactory, arguments) .then(function(result) { self.expector.processResult(result, errorForStack); }); }; } function addCoreMatchers(prototype, matchers, wrapper) { for (var matcherName in matchers) { var matcher = matchers[matcherName]; prototype[matcherName] = wrapper(matcherName, matcher); } } function addFilter(source, filter) { var result = Object.create(source); result.expector = source.expector.addFilter(filter); return result; } function negatedFailureMessage(result, matcherName, args, matchersUtil) { if (result.message) { if (j$.isFunction_(result.message)) { return result.message(); } else { return result.message; } } args = args.slice(); args.unshift(true); args.unshift(matcherName); return matchersUtil.buildFailureMessage.apply(matchersUtil, args); } function negate(result) { result.pass = !result.pass; return result; } var syncNegatingFilter = { selectComparisonFunc: function(matcher) { function defaultNegativeCompare() { return negate(matcher.compare.apply(null, arguments)); } return matcher.negativeCompare || defaultNegativeCompare; }, buildFailureMessage: negatedFailureMessage }; var asyncNegatingFilter = { selectComparisonFunc: function(matcher) { function defaultNegativeCompare() { return matcher.compare.apply(this, arguments).then(negate); } return matcher.negativeCompare || defaultNegativeCompare; }, buildFailureMessage: negatedFailureMessage }; function ContextAddingFilter(message) { this.message = message; } ContextAddingFilter.prototype.modifyFailureMessage = function(msg) { var nl = msg.indexOf('\n'); if (nl === -1) { return this.message + ': ' + msg; } else { return this.message + ':\n' + indent(msg); } }; function indent(s) { return s.replace(/^/gm, ' '); } return { factory: function(options) { return new Expectation(options || {}); }, addCoreMatchers: function(matchers) { addCoreMatchers(Expectation.prototype, matchers, wrapSyncCompare); }, asyncFactory: function(options) { return new AsyncExpectation(options || {}); }, addAsyncCoreMatchers: function(matchers) { addCoreMatchers(AsyncExpectation.prototype, matchers, wrapAsyncCompare); } }; }; getJasmineRequireObj().ExpectationFilterChain = function() { function ExpectationFilterChain(maybeFilter, prev) { this.filter_ = maybeFilter; this.prev_ = prev; } ExpectationFilterChain.prototype.addFilter = function(filter) { return new ExpectationFilterChain(filter, this); }; ExpectationFilterChain.prototype.selectComparisonFunc = function(matcher) { return this.callFirst_('selectComparisonFunc', arguments).result || undefined; }; ExpectationFilterChain.prototype.buildFailureMessage = function( result, matcherName, args, matchersUtil ) { return this.callFirst_('buildFailureMessage', arguments).result || undefined; }; ExpectationFilterChain.prototype.modifyFailureMessage = function(msg) { var result = this.callFirst_('modifyFailureMessage', arguments).result; return result || msg; }; ExpectationFilterChain.prototype.callFirst_ = function(fname, args) { var prevResult; if (this.prev_) { prevResult = this.prev_.callFirst_(fname, args); if (prevResult.found) { return prevResult; } } if (this.filter_ && this.filter_[fname]) { return { found: true, result: this.filter_[fname].apply(this.filter_, args) }; } return { found: false }; }; return ExpectationFilterChain; }; //TODO: expectation result may make more sense as a presentation of an expectation. getJasmineRequireObj().buildExpectationResult = function(j$) { function buildExpectationResult(options) { var messageFormatter = options.messageFormatter || function() {}, stackFormatter = options.stackFormatter || function() {}; /** * @typedef Expectation * @property {String} matcherName - The name of the matcher that was executed for this expectation. * @property {String} message - The failure message for the expectation. * @property {String} stack - The stack trace for the failure if available. * @property {Boolean} passed - Whether the expectation passed or failed. * @property {Object} expected - If the expectation failed, what was the expected value. * @property {Object} actual - If the expectation failed, what actual value was produced. */ var result = { matcherName: options.matcherName, message: message(), stack: stack(), passed: options.passed }; if (!result.passed) { result.expected = options.expected; result.actual = options.actual; if (options.error && !j$.isString_(options.error)) { if ('code' in options.error) { result.code = options.error.code; } if ( options.error.code === 'ERR_ASSERTION' && options.expected === '' && options.actual === '' ) { result.expected = options.error.expected; result.actual = options.error.actual; result.matcherName = 'assert ' + options.error.operator; } } } return result; function message() { if (options.passed) { return 'Passed.'; } else if (options.message) { return options.message; } else if (options.error) { return messageFormatter(options.error); } return ''; } function stack() { if (options.passed) { return ''; } var error = options.error; if (!error) { if (options.errorForStack) { error = options.errorForStack; } else if (options.stack) { error = options; } else { try { throw new Error(message()); } catch (e) { error = e; } } } return stackFormatter(error); } } return buildExpectationResult; }; getJasmineRequireObj().Expector = function(j$) { function Expector(options) { this.matchersUtil = options.matchersUtil || { buildFailureMessage: function() {} }; this.customEqualityTesters = options.customEqualityTesters || []; this.actual = options.actual; this.addExpectationResult = options.addExpectationResult || function() {}; this.filters = new j$.ExpectationFilterChain(); } Expector.prototype.instantiateMatcher = function( matcherName, matcherFactory, args ) { this.matcherName = matcherName; this.args = Array.prototype.slice.call(args, 0); this.expected = this.args.slice(0); this.args.unshift(this.actual); var matcher = matcherFactory(this.matchersUtil, this.customEqualityTesters); var comparisonFunc = this.filters.selectComparisonFunc(matcher); return comparisonFunc || matcher.compare; }; Expector.prototype.buildMessage = function(result) { var self = this; if (result.pass) { return ''; } var msg = this.filters.buildFailureMessage( result, this.matcherName, this.args, this.matchersUtil, defaultMessage ); return this.filters.modifyFailureMessage(msg || defaultMessage()); function defaultMessage() { if (!result.message) { var args = self.args.slice(); args.unshift(false); args.unshift(self.matcherName); return self.matchersUtil.buildFailureMessage.apply( self.matchersUtil, args ); } else if (j$.isFunction_(result.message)) { return result.message(); } else { return result.message; } } }; Expector.prototype.compare = function(matcherName, matcherFactory, args) { var matcherCompare = this.instantiateMatcher( matcherName, matcherFactory, args ); return matcherCompare.apply(null, this.args); }; Expector.prototype.addFilter = function(filter) { var result = Object.create(this); result.filters = this.filters.addFilter(filter); return result; }; Expector.prototype.processResult = function(result, errorForStack) { var message = this.buildMessage(result); if (this.expected.length === 1) { this.expected = this.expected[0]; } this.addExpectationResult(result.pass, { matcherName: this.matcherName, passed: result.pass, message: message, error: errorForStack ? undefined : result.error, errorForStack: errorForStack || undefined, actual: this.actual, expected: this.expected // TODO: this may need to be arrayified/sliced }); }; return Expector; }; getJasmineRequireObj().formatErrorMsg = function() { function generateErrorMsg(domain, usage) { var usageDefinition = usage ? '\nUsage: ' + usage : ''; return function errorMsg(msg) { return domain + ' : ' + msg + usageDefinition; }; } return generateErrorMsg; }; getJasmineRequireObj().GlobalErrors = function(j$) { function GlobalErrors(global) { var handlers = []; global = global || j$.getGlobal(); var onerror = function onerror() { var handler = handlers[handlers.length - 1]; if (handler) { handler.apply(null, Array.prototype.slice.call(arguments, 0)); } else { throw arguments[0]; } }; this.originalHandlers = {}; this.jasmineHandlers = {}; this.installOne_ = function installOne_(errorType, jasmineMessage) { function taggedOnError(error) { error.jasmineMessage = jasmineMessage + ': ' + error; var handler = handlers[handlers.length - 1]; if (handler) { handler(error); } else { throw error; } } this.originalHandlers[errorType] = global.process.listeners(errorType); this.jasmineHandlers[errorType] = taggedOnError; global.process.removeAllListeners(errorType); global.process.on(errorType, taggedOnError); this.uninstall = function uninstall() { var errorTypes = Object.keys(this.originalHandlers); for (var iType = 0; iType < errorTypes.length; iType++) { var errorType = errorTypes[iType]; global.process.removeListener( errorType, this.jasmineHandlers[errorType] ); for (var i = 0; i < this.originalHandlers[errorType].length; i++) { global.process.on(errorType, this.originalHandlers[errorType][i]); } delete this.originalHandlers[errorType]; delete this.jasmineHandlers[errorType]; } }; }; this.install = function install() { if ( global.process && global.process.listeners && j$.isFunction_(global.process.on) ) { this.installOne_('uncaughtException', 'Uncaught exception'); this.installOne_('unhandledRejection', 'Unhandled promise rejection'); } else { var originalHandler = global.onerror; global.onerror = onerror; var browserRejectionHandler = function browserRejectionHandler(event) { if (j$.isError_(event.reason)) { event.reason.jasmineMessage = 'Unhandled promise rejection: ' + event.reason; onerror(event.reason); } else { onerror('Unhandled promise rejection: ' + event.reason); } }; if (global.addEventListener) { global.addEventListener( 'unhandledrejection', browserRejectionHandler ); } this.uninstall = function uninstall() { global.onerror = originalHandler; if (global.removeEventListener) { global.removeEventListener( 'unhandledrejection', browserRejectionHandler ); } }; } }; this.pushListener = function pushListener(listener) { handlers.push(listener); }; this.popListener = function popListener() { handlers.pop(); }; } return GlobalErrors; }; /* eslint-disable compat/compat */ getJasmineRequireObj().toBePending = function(j$) { /** * Expect a promise to be pending, ie. the promise is neither resolved nor rejected. * @function * @async * @name async-matchers#toBePending * @since 3.6 * @example * await expectAsync(aPromise).toBePending(); */ return function toBePending() { return { compare: function(actual) { if (!j$.isPromiseLike(actual)) { throw new Error('Expected toBePending to be called on a promise.'); } var want = {}; return Promise.race([actual, Promise.resolve(want)]).then( function(got) { return {pass: want === got}; }, function() { return {pass: false}; } ); } }; }; }; getJasmineRequireObj().toBeRejected = function(j$) { /** * Expect a promise to be rejected. * @function * @async * @name async-matchers#toBeRejected * @since 3.1.0 * @example * await expectAsync(aPromise).toBeRejected(); * @example * return expectAsync(aPromise).toBeRejected(); */ return function toBeRejected() { return { compare: function(actual) { if (!j$.isPromiseLike(actual)) { throw new Error('Expected toBeRejected to be called on a promise.'); } return actual.then( function() { return {pass: false}; }, function() { return {pass: true}; } ); } }; }; }; getJasmineRequireObj().toBeRejectedWith = function(j$) { /** * Expect a promise to be rejected with a value equal to the expected, using deep equality comparison. * @function * @async * @name async-matchers#toBeRejectedWith * @since 3.3.0 * @param {Object} expected - Value that the promise is expected to be rejected with * @example * await expectAsync(aPromise).toBeRejectedWith({prop: 'value'}); * @example * return expectAsync(aPromise).toBeRejectedWith({prop: 'value'}); */ return function toBeRejectedWith(matchersUtil) { return { compare: function(actualPromise, expectedValue) { if (!j$.isPromiseLike(actualPromise)) { throw new Error('Expected toBeRejectedWith to be called on a promise.'); } function prefix(passed) { return 'Expected a promise ' + (passed ? 'not ' : '') + 'to be rejected with ' + matchersUtil.pp(expectedValue); } return actualPromise.then( function() { return { pass: false, message: prefix(false) + ' but it was resolved.' }; }, function(actualValue) { if (matchersUtil.equals(actualValue, expectedValue)) { return { pass: true, message: prefix(true) + '.' }; } else { return { pass: false, message: prefix(false) + ' but it was rejected with ' + matchersUtil.pp(actualValue) + '.' }; } } ); } }; }; }; getJasmineRequireObj().toBeRejectedWithError = function(j$) { /** * Expect a promise to be rejected with a value matched to the expected * @function * @async * @name async-matchers#toBeRejectedWithError * @since 3.5.0 * @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used. * @param {RegExp|String} [message] - The message that should be set on the thrown `Error` * @example * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, 'Error message'); * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, /Error message/); * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError); * await expectAsync(aPromise).toBeRejectedWithError('Error message'); * return expectAsync(aPromise).toBeRejectedWithError(/Error message/); */ return function toBeRejectedWithError(matchersUtil) { return { compare: function(actualPromise, arg1, arg2) { if (!j$.isPromiseLike(actualPromise)) { throw new Error('Expected toBeRejectedWithError to be called on a promise.'); } var expected = getExpectedFromArgs(arg1, arg2, matchersUtil); return actualPromise.then( function() { return { pass: false, message: 'Expected a promise to be rejected but it was resolved.' }; }, function(actualValue) { return matchError(actualValue, expected, matchersUtil); } ); } }; }; function matchError(actual, expected, matchersUtil) { if (!j$.isError_(actual)) { return fail(expected, 'rejected with ' + matchersUtil.pp(actual)); } if (!(actual instanceof expected.error)) { return fail(expected, 'rejected with type ' + j$.fnNameFor(actual.constructor)); } var actualMessage = actual.message; if (actualMessage === expected.message || typeof expected.message === 'undefined') { return pass(expected); } if (expected.message instanceof RegExp && expected.message.test(actualMessage)) { return pass(expected); } return fail(expected, 'rejected with ' + matchersUtil.pp(actual)); } function pass(expected) { return { pass: true, message: 'Expected a promise not to be rejected with ' + expected.printValue + ', but it was.' }; } function fail(expected, message) { return { pass: false, message: 'Expected a promise to be rejected with ' + expected.printValue + ' but it was ' + message + '.' }; } function getExpectedFromArgs(arg1, arg2, matchersUtil) { var error, message; if (isErrorConstructor(arg1)) { error = arg1; message = arg2; } else { error = Error; message = arg1; } return { error: error, message: message, printValue: j$.fnNameFor(error) + (typeof message === 'undefined' ? '' : ': ' + matchersUtil.pp(message)) }; } function isErrorConstructor(value) { return typeof value === 'function' && (value === Error || j$.isError_(value.prototype)); } }; getJasmineRequireObj().toBeResolved = function(j$) { /** * Expect a promise to be resolved. * @function * @async * @name async-matchers#toBeResolved * @since 3.1.0 * @example * await expectAsync(aPromise).toBeResolved(); * @example * return expectAsync(aPromise).toBeResolved(); */ return function toBeResolved() { return { compare: function(actual) { if (!j$.isPromiseLike(actual)) { throw new Error('Expected toBeResolved to be called on a promise.'); } return actual.then( function() { return {pass: true}; }, function() { return {pass: false}; } ); } }; }; }; getJasmineRequireObj().toBeResolvedTo = function(j$) { /** * Expect a promise to be resolved to a value equal to the expected, using deep equality comparison. * @function * @async * @name async-matchers#toBeResolvedTo * @since 3.1.0 * @param {Object} expected - Value that the promise is expected to resolve to * @example * await expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); * @example * return expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); */ return function toBeResolvedTo(matchersUtil) { return { compare: function(actualPromise, expectedValue) { if (!j$.isPromiseLike(actualPromise)) { throw new Error('Expected toBeResolvedTo to be called on a promise.'); } function prefix(passed) { return 'Expected a promise ' + (passed ? 'not ' : '') + 'to be resolved to ' + matchersUtil.pp(expectedValue); } return actualPromise.then( function(actualValue) { if (matchersUtil.equals(actualValue, expectedValue)) { return { pass: true, message: prefix(true) + '.' }; } else { return { pass: false, message: prefix(false) + ' but it was resolved to ' + matchersUtil.pp(actualValue) + '.' }; } }, function() { return { pass: false, message: prefix(false) + ' but it was rejected.' }; } ); } }; }; }; getJasmineRequireObj().DiffBuilder = function (j$) { return function DiffBuilder(config) { var prettyPrinter = (config || {}).prettyPrinter || j$.makePrettyPrinter(), mismatches = new j$.MismatchTree(), path = new j$.ObjectPath(), actualRoot = undefined, expectedRoot = undefined; return { setRoots: function (actual, expected) { actualRoot = actual; expectedRoot = expected; }, recordMismatch: function (formatter) { mismatches.add(path, formatter); }, getMessage: function () { var messages = []; mismatches.traverse(function (path, isLeaf, formatter) { var actualCustom, expectedCustom, useCustom, derefResult = dereferencePath(path, actualRoot, expectedRoot, prettyPrinter), actual = derefResult.actual, expected = derefResult.expected; if (formatter) { messages.push(formatter(actual, expected, path, prettyPrinter)); return true; } actualCustom = prettyPrinter.customFormat_(actual); expectedCustom = prettyPrinter.customFormat_(expected); useCustom = !(j$.util.isUndefined(actualCustom) && j$.util.isUndefined(expectedCustom)); if (useCustom) { messages.push(wrapPrettyPrinted(actualCustom, expectedCustom, path)); return false; // don't recurse further } if (isLeaf) { messages.push(defaultFormatter(actual, expected, path, prettyPrinter)); } return true; }); return messages.join('\n'); }, withPath: function (pathComponent, block) { var oldPath = path; path = path.add(pathComponent); block(); path = oldPath; } }; function defaultFormatter(actual, expected, path, prettyPrinter) { return wrapPrettyPrinted(prettyPrinter(actual), prettyPrinter(expected), path); } function wrapPrettyPrinted(actual, expected, path) { return 'Expected ' + path + (path.depth() ? ' = ' : '') + actual + ' to equal ' + expected + '.'; } }; function dereferencePath(objectPath, actual, expected, pp) { function handleAsymmetricExpected() { if (j$.isAsymmetricEqualityTester_(expected) && j$.isFunction_(expected.valuesForDiff_)) { var asymmetricResult = expected.valuesForDiff_(actual, pp); expected = asymmetricResult.self; actual = asymmetricResult.other; } } var i; handleAsymmetricExpected(); for (i = 0; i < objectPath.components.length; i++) { actual = actual[objectPath.components[i]]; expected = expected[objectPath.components[i]]; handleAsymmetricExpected(); } return {actual: actual, expected: expected}; } }; getJasmineRequireObj().MatchersUtil = function(j$) { // TODO: convert all uses of j$.pp to use the injected pp /** * _Note:_ Do not construct this directly. Jasmine will construct one and * pass it to matchers and asymmetric equality testers. * @name MatchersUtil * @classdesc Utilities for use in implementing matchers * @constructor */ function MatchersUtil(options) { options = options || {}; this.customTesters_ = options.customTesters || []; /** * Formats a value for use in matcher failure messages and similar contexts, * taking into account the current set of custom value formatters. * @function * @name MatchersUtil#pp * @since 3.6.0 * @param {*} value The value to pretty-print * @return {string} The pretty-printed value */ this.pp = options.pp || function() {}; }; /** * Determines whether `haystack` contains `needle`, using the same comparison * logic as {@link MatchersUtil#equals}. * @function * @name MatchersUtil#contains * @since 2.0.0 * @param {*} haystack The collection to search * @param {*} needle The value to search for * @param [customTesters] An array of custom equality testers * @returns {boolean} True if `needle` was found in `haystack` */ MatchersUtil.prototype.contains = function(haystack, needle, customTesters) { if (j$.isSet(haystack)) { return haystack.has(needle); } if ((Object.prototype.toString.apply(haystack) === '[object Array]') || (!!haystack && !haystack.indexOf)) { for (var i = 0; i < haystack.length; i++) { if (this.equals(haystack[i], needle, customTesters)) { return true; } } return false; } return !!haystack && haystack.indexOf(needle) >= 0; }; MatchersUtil.prototype.buildFailureMessage = function() { var self = this; var args = Array.prototype.slice.call(arguments, 0), matcherName = args[0], isNot = args[1], actual = args[2], expected = args.slice(3), englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); var message = 'Expected ' + self.pp(actual) + (isNot ? ' not ' : ' ') + englishyPredicate; if (expected.length > 0) { for (var i = 0; i < expected.length; i++) { if (i > 0) { message += ','; } message += ' ' + self.pp(expected[i]); } } return message + '.'; }; MatchersUtil.prototype.asymmetricDiff_ = function(a, b, aStack, bStack, customTesters, diffBuilder) { if (j$.isFunction_(b.valuesForDiff_)) { var values = b.valuesForDiff_(a, this.pp); this.eq_(values.other, values.self, aStack, bStack, customTesters, diffBuilder); } else { diffBuilder.recordMismatch(); } }; MatchersUtil.prototype.asymmetricMatch_ = function(a, b, aStack, bStack, customTesters, diffBuilder) { var asymmetricA = j$.isAsymmetricEqualityTester_(a), asymmetricB = j$.isAsymmetricEqualityTester_(b), shim, result; if (asymmetricA === asymmetricB) { return undefined; } shim = j$.asymmetricEqualityTesterArgCompatShim(this, customTesters); if (asymmetricA) { result = a.asymmetricMatch(b, shim); if (!result) { diffBuilder.recordMismatch(); } return result; } if (asymmetricB) { result = b.asymmetricMatch(a, shim); if (!result) { this.asymmetricDiff_(a, b, aStack, bStack, customTesters, diffBuilder); } return result; } }; /** * Determines whether two values are deeply equal to each other. * @function * @name MatchersUtil#equals * @since 2.0.0 * @param {*} a The first value to compare * @param {*} b The second value to compare * @param [customTesters] An array of custom equality testers * @returns {boolean} True if the values are equal */ MatchersUtil.prototype.equals = function(a, b, customTestersOrDiffBuilder, diffBuilderOrNothing) { var customTesters, diffBuilder; if (isDiffBuilder(customTestersOrDiffBuilder)) { diffBuilder = customTestersOrDiffBuilder; } else { customTesters = customTestersOrDiffBuilder; diffBuilder = diffBuilderOrNothing; } customTesters = customTesters || this.customTesters_; diffBuilder = diffBuilder || j$.NullDiffBuilder(); diffBuilder.setRoots(a, b); return this.eq_(a, b, [], [], customTesters, diffBuilder); }; // Equality function lovingly adapted from isEqual in // [Underscore](http://underscorejs.org) MatchersUtil.prototype.eq_ = function(a, b, aStack, bStack, customTesters, diffBuilder) { var result = true, self = this, i; var asymmetricResult = this.asymmetricMatch_(a, b, aStack, bStack, customTesters, diffBuilder); if (!j$.util.isUndefined(asymmetricResult)) { return asymmetricResult; } for (i = 0; i < customTesters.length; i++) { var customTesterResult = customTesters[i](a, b); if (!j$.util.isUndefined(customTesterResult)) { if (!customTesterResult) { diffBuilder.recordMismatch(); } return customTesterResult; } } if (a instanceof Error && b instanceof Error) { result = a.message == b.message; if (!result) { diffBuilder.recordMismatch(); } return result; } // Identical objects are equal. `0 === -0`, but they aren't identical. // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). if (a === b) { result = a !== 0 || 1 / a == 1 / b; if (!result) { diffBuilder.recordMismatch(); } return result; } // A strict comparison is necessary because `null == undefined`. if (a === null || b === null) { result = a === b; if (!result) { diffBuilder.recordMismatch(); } return result; } var className = Object.prototype.toString.call(a); if (className != Object.prototype.toString.call(b)) { diffBuilder.recordMismatch(); return false; } switch (className) { // Strings, numbers, dates, and booleans are compared by value. case '[object String]': // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is // equivalent to `new String("5")`. result = a == String(b); if (!result) { diffBuilder.recordMismatch(); } return result; case '[object Number]': // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for // other numeric values. result = a != +a ? b != +b : (a === 0 && b === 0 ? 1 / a == 1 / b : a == +b); if (!result) { diffBuilder.recordMismatch(); } return result; case '[object Date]': case '[object Boolean]': // Coerce dates and booleans to numeric primitive values. Dates are compared by their // millisecond representations. Note that invalid dates with millisecond representations // of `NaN` are not equivalent. result = +a == +b; if (!result) { diffBuilder.recordMismatch(); } return result; // RegExps are compared by their source patterns and flags. case '[object RegExp]': return a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase; } if (typeof a != 'object' || typeof b != 'object') { diffBuilder.recordMismatch(); return false; } var aIsDomNode = j$.isDomNode(a); var bIsDomNode = j$.isDomNode(b); if (aIsDomNode && bIsDomNode) { // At first try to use DOM3 method isEqualNode result = a.isEqualNode(b); if (!result) { diffBuilder.recordMismatch(); } return result; } if (aIsDomNode || bIsDomNode) { diffBuilder.recordMismatch(); return false; } var aIsPromise = j$.isPromise(a); var bIsPromise = j$.isPromise(b); if (aIsPromise && bIsPromise) { return a === b; } // Assume equality for cyclic structures. The algorithm for detecting cyclic // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. var length = aStack.length; while (length--) { // Linear search. Performance is inversely proportional to the number of // unique nested structures. if (aStack[length] == a) { return bStack[length] == b; } } // Add the first object to the stack of traversed objects. aStack.push(a); bStack.push(b); var size = 0; // Recursively compare objects and arrays. // Compare array lengths to determine if a deep comparison is necessary. if (className == '[object Array]') { var aLength = a.length; var bLength = b.length; diffBuilder.withPath('length', function() { if (aLength !== bLength) { diffBuilder.recordMismatch(); result = false; } }); for (i = 0; i < aLength || i < bLength; i++) { diffBuilder.withPath(i, function() { if (i >= bLength) { diffBuilder.recordMismatch(actualArrayIsLongerFormatter.bind(null, self.pp)); result = false; } else { result = self.eq_(i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0, aStack, bStack, customTesters, diffBuilder) && result; } }); } if (!result) { return false; } } else if (j$.isMap(a) && j$.isMap(b)) { if (a.size != b.size) { diffBuilder.recordMismatch(); return false; } var keysA = []; var keysB = []; a.forEach( function( valueA, keyA ) { keysA.push( keyA ); }); b.forEach( function( valueB, keyB ) { keysB.push( keyB ); }); // For both sets of keys, check they map to equal values in both maps. // Keep track of corresponding keys (in insertion order) in order to handle asymmetric obj keys. var mapKeys = [keysA, keysB]; var cmpKeys = [keysB, keysA]; var mapIter, mapKey, mapValueA, mapValueB; var cmpIter, cmpKey; for (i = 0; result && i < mapKeys.length; i++) { mapIter = mapKeys[i]; cmpIter = cmpKeys[i]; for (var j = 0; result && j < mapIter.length; j++) { mapKey = mapIter[j]; cmpKey = cmpIter[j]; mapValueA = a.get(mapKey); // Only use the cmpKey when one of the keys is asymmetric and the corresponding key matches, // otherwise explicitly look up the mapKey in the other Map since we want keys with unique // obj identity (that are otherwise equal) to not match. if (j$.isAsymmetricEqualityTester_(mapKey) || j$.isAsymmetricEqualityTester_(cmpKey) && this.eq_(mapKey, cmpKey, aStack, bStack, customTesters, j$.NullDiffBuilder())) { mapValueB = b.get(cmpKey); } else { mapValueB = b.get(mapKey); } result = this.eq_(mapValueA, mapValueB, aStack, bStack, customTesters, j$.NullDiffBuilder()); } } if (!result) { diffBuilder.recordMismatch(); return false; } } else if (j$.isSet(a) && j$.isSet(b)) { if (a.size != b.size) { diffBuilder.recordMismatch(); return false; } var valuesA = []; a.forEach( function( valueA ) { valuesA.push( valueA ); }); var valuesB = []; b.forEach( function( valueB ) { valuesB.push( valueB ); }); // For both sets, check they are all contained in the other set var setPairs = [[valuesA, valuesB], [valuesB, valuesA]]; var stackPairs = [[aStack, bStack], [bStack, aStack]]; var baseValues, baseValue, baseStack; var otherValues, otherValue, otherStack; var found; var prevStackSize; for (i = 0; result && i < setPairs.length; i++) { baseValues = setPairs[i][0]; otherValues = setPairs[i][1]; baseStack = stackPairs[i][0]; otherStack = stackPairs[i][1]; // For each value in the base set... for (var k = 0; result && k < baseValues.length; k++) { baseValue = baseValues[k]; found = false; // ... test that it is present in the other set for (var l = 0; !found && l < otherValues.length; l++) { otherValue = otherValues[l]; prevStackSize = baseStack.length; // compare by value equality found = this.eq_(baseValue, otherValue, baseStack, otherStack, customTesters, j$.NullDiffBuilder()); if (!found && prevStackSize !== baseStack.length) { baseStack.splice(prevStackSize); otherStack.splice(prevStackSize); } } result = result && found; } } if (!result) { diffBuilder.recordMismatch(); return false; } } else { // Objects with different constructors are not equivalent, but `Object`s // or `Array`s from different frames are. var aCtor = a.constructor, bCtor = b.constructor; if (aCtor !== bCtor && isFunction(aCtor) && isFunction(bCtor) && a instanceof aCtor && b instanceof bCtor && !(aCtor instanceof aCtor && bCtor instanceof bCtor)) { diffBuilder.recordMismatch(constructorsAreDifferentFormatter.bind(null, this.pp)); return false; } } // Deep compare objects. var aKeys = keys(a, className == '[object Array]'), key; size = aKeys.length; // Ensure that both objects contain the same number of properties before comparing deep equality. if (keys(b, className == '[object Array]').length !== size) { diffBuilder.recordMismatch(objectKeysAreDifferentFormatter.bind(null, this.pp)); return false; } for (i = 0; i < size; i++) { key = aKeys[i]; // Deep compare each member if (!j$.util.has(b, key)) { diffBuilder.recordMismatch(objectKeysAreDifferentFormatter.bind(null, this.pp)); result = false; continue; } diffBuilder.withPath(key, function() { if(!self.eq_(a[key], b[key], aStack, bStack, customTesters, diffBuilder)) { result = false; } }); } if (!result) { return false; } // Remove the first object from the stack of traversed objects. aStack.pop(); bStack.pop(); return result; }; function keys(obj, isArray) { var allKeys = Object.keys ? Object.keys(obj) : (function(o) { var keys = []; for (var key in o) { if (j$.util.has(o, key)) { keys.push(key); } } return keys; })(obj); if (!isArray) { return allKeys; } if (allKeys.length === 0) { return allKeys; } var extraKeys = []; for (var i = 0; i < allKeys.length; i++) { if (!/^[0-9]+$/.test(allKeys[i])) { extraKeys.push(allKeys[i]); } } return extraKeys; } function isFunction(obj) { return typeof obj === 'function'; } function objectKeysAreDifferentFormatter(pp, actual, expected, path) { var missingProperties = j$.util.objectDifference(expected, actual), extraProperties = j$.util.objectDifference(actual, expected), missingPropertiesMessage = formatKeyValuePairs(pp, missingProperties), extraPropertiesMessage = formatKeyValuePairs(pp, extraProperties), messages = []; if (!path.depth()) { path = 'object'; } if (missingPropertiesMessage.length) { messages.push('Expected ' + path + ' to have properties' + missingPropertiesMessage); } if (extraPropertiesMessage.length) { messages.push('Expected ' + path + ' not to have properties' + extraPropertiesMessage); } return messages.join('\n'); } function constructorsAreDifferentFormatter(pp, actual, expected, path) { if (!path.depth()) { path = 'object'; } return 'Expected ' + path + ' to be a kind of ' + j$.fnNameFor(expected.constructor) + ', but was ' + pp(actual) + '.'; } function actualArrayIsLongerFormatter(pp, actual, expected, path) { return 'Unexpected ' + path + (path.depth() ? ' = ' : '') + pp(actual) + ' in array.'; } function formatKeyValuePairs(pp, obj) { var formatted = ''; for (var key in obj) { formatted += '\n ' + key + ': ' + pp(obj[key]); } return formatted; } function isDiffBuilder(obj) { return obj && typeof obj.recordMismatch === 'function'; } return MatchersUtil; }; getJasmineRequireObj().MismatchTree = function (j$) { /* To be able to apply custom object formatters at all possible levels of an object graph, DiffBuilder needs to be able to know not just where the mismatch occurred but also all ancestors of the mismatched value in both the expected and actual object graphs. MismatchTree maintains that context and provides it via the traverse method. */ function MismatchTree(path) { this.path = path || new j$.ObjectPath([]); this.formatter = undefined; this.children = []; this.isMismatch = false; } MismatchTree.prototype.add = function (path, formatter) { var key, child; if (path.depth() === 0) { this.formatter = formatter; this.isMismatch = true; } else { key = path.components[0]; path = path.shift(); child = this.child(key); if (!child) { child = new MismatchTree(this.path.add(key)); this.children.push(child); } child.add(path, formatter); } }; MismatchTree.prototype.traverse = function (visit) { var i, hasChildren = this.children.length > 0; if (this.isMismatch || hasChildren) { if (visit(this.path, !hasChildren, this.formatter)) { for (i = 0; i < this.children.length; i++) { this.children[i].traverse(visit); } } } }; MismatchTree.prototype.child = function(key) { var i, pathEls; for (i = 0; i < this.children.length; i++) { pathEls = this.children[i].path.components; if (pathEls[pathEls.length - 1] === key) { return this.children[i]; } } }; return MismatchTree; }; getJasmineRequireObj().nothing = function() { /** * {@link expect} nothing explicitly. * @function * @name matchers#nothing * @since 2.8.0 * @example * expect().nothing(); */ function nothing() { return { compare: function() { return { pass: true }; } }; } return nothing; }; getJasmineRequireObj().NullDiffBuilder = function(j$) { return function() { return { withPath: function(_, block) { block(); }, setRoots: function() {}, recordMismatch: function() {} }; }; }; getJasmineRequireObj().ObjectPath = function(j$) { function ObjectPath(components) { this.components = components || []; } ObjectPath.prototype.toString = function() { if (this.components.length) { return '$' + map(this.components, formatPropertyAccess).join(''); } else { return ''; } }; ObjectPath.prototype.add = function(component) { return new ObjectPath(this.components.concat([component])); }; ObjectPath.prototype.shift = function() { return new ObjectPath(this.components.slice(1)); }; ObjectPath.prototype.depth = function() { return this.components.length; }; function formatPropertyAccess(prop) { if (typeof prop === 'number') { return '[' + prop + ']'; } if (isValidIdentifier(prop)) { return '.' + prop; } return '[\'' + prop + '\']'; } function map(array, fn) { var results = []; for (var i = 0; i < array.length; i++) { results.push(fn(array[i])); } return results; } function isValidIdentifier(string) { return /^[A-Za-z\$_][A-Za-z0-9\$_]*$/.test(string); } return ObjectPath; }; getJasmineRequireObj().requireAsyncMatchers = function(jRequire, j$) { var availableMatchers = [ 'toBePending', 'toBeResolved', 'toBeRejected', 'toBeResolvedTo', 'toBeRejectedWith', 'toBeRejectedWithError' ], matchers = {}; for (var i = 0; i < availableMatchers.length; i++) { var name = availableMatchers[i]; matchers[name] = jRequire[name](j$); } return matchers; }; getJasmineRequireObj().toBe = function(j$) { /** * {@link expect} the actual value to be `===` to the expected value. * @function * @name matchers#toBe * @since 1.3.0 * @param {Object} expected - The expected value to compare against. * @example * expect(thing).toBe(realThing); */ function toBe(matchersUtil) { var tip = ' Tip: To check for deep equality, use .toEqual() instead of .toBe().'; return { compare: function(actual, expected) { var result = { pass: actual === expected }; if (typeof expected === 'object') { result.message = matchersUtil.buildFailureMessage('toBe', result.pass, actual, expected) + tip; } return result; } }; } return toBe; }; getJasmineRequireObj().toBeCloseTo = function() { /** * {@link expect} the actual value to be within a specified precision of the expected value. * @function * @name matchers#toBeCloseTo * @since 1.3.0 * @param {Object} expected - The expected value to compare against. * @param {Number} [precision=2] - The number of decimal points to check. * @example * expect(number).toBeCloseTo(42.2, 3); */ function toBeCloseTo() { return { compare: function(actual, expected, precision) { if (precision !== 0) { precision = precision || 2; } if (expected === null || actual === null) { throw new Error('Cannot use toBeCloseTo with null. Arguments evaluated to: ' + 'expect(' + actual + ').toBeCloseTo(' + expected + ').' ); } var pow = Math.pow(10, precision + 1); var delta = Math.abs(expected - actual); var maxDelta = Math.pow(10, -precision) / 2; return { pass: Math.round(delta * pow) <= maxDelta * pow }; } }; } return toBeCloseTo; }; getJasmineRequireObj().toBeDefined = function() { /** * {@link expect} the actual value to be defined. (Not `undefined`) * @function * @name matchers#toBeDefined * @since 1.3.0 * @example * expect(result).toBeDefined(); */ function toBeDefined() { return { compare: function(actual) { return { pass: (void 0 !== actual) }; } }; } return toBeDefined; }; getJasmineRequireObj().toBeFalse = function() { /** * {@link expect} the actual value to be `false`. * @function * @name matchers#toBeFalse * @since 3.5.0 * @example * expect(result).toBeFalse(); */ function toBeFalse() { return { compare: function(actual) { return { pass: actual === false }; } }; } return toBeFalse; }; getJasmineRequireObj().toBeFalsy = function() { /** * {@link expect} the actual value to be falsy * @function * @name matchers#toBeFalsy * @since 2.0.0 * @example * expect(result).toBeFalsy(); */ function toBeFalsy() { return { compare: function(actual) { return { pass: !actual }; } }; } return toBeFalsy; }; getJasmineRequireObj().toBeGreaterThan = function() { /** * {@link expect} the actual value to be greater than the expected value. * @function * @name matchers#toBeGreaterThan * @since 2.0.0 * @param {Number} expected - The value to compare against. * @example * expect(result).toBeGreaterThan(3); */ function toBeGreaterThan() { return { compare: function(actual, expected) { return { pass: actual > expected }; } }; } return toBeGreaterThan; }; getJasmineRequireObj().toBeGreaterThanOrEqual = function() { /** * {@link expect} the actual value to be greater than or equal to the expected value. * @function * @name matchers#toBeGreaterThanOrEqual * @since 2.0.0 * @param {Number} expected - The expected value to compare against. * @example * expect(result).toBeGreaterThanOrEqual(25); */ function toBeGreaterThanOrEqual() { return { compare: function(actual, expected) { return { pass: actual >= expected }; } }; } return toBeGreaterThanOrEqual; }; getJasmineRequireObj().toBeInstanceOf = function(j$) { var usageError = j$.formatErrorMsg('', 'expect(value).toBeInstanceOf()'); /** * {@link expect} the actual to be an instance of the expected class * @function * @name matchers#toBeInstanceOf * @since 3.5.0 * @param {Object} expected - The class or constructor function to check for * @example * expect('foo').toBeInstanceOf(String); * expect(3).toBeInstanceOf(Number); * expect(new Error()).toBeInstanceOf(Error); */ function toBeInstanceOf(matchersUtil) { return { compare: function(actual, expected) { var actualType = actual && actual.constructor ? j$.fnNameFor(actual.constructor) : matchersUtil.pp(actual), expectedType = expected ? j$.fnNameFor(expected) : matchersUtil.pp(expected), expectedMatcher, pass; try { expectedMatcher = new j$.Any(expected); pass = expectedMatcher.asymmetricMatch(actual); } catch (error) { throw new Error(usageError('Expected value is not a constructor function')); } if (pass) { return { pass: true, message: 'Expected instance of ' + actualType + ' not to be an instance of ' + expectedType }; } else { return { pass: false, message: 'Expected instance of ' + actualType + ' to be an instance of ' + expectedType }; } } }; } return toBeInstanceOf; }; getJasmineRequireObj().toBeLessThan = function() { /** * {@link expect} the actual value to be less than the expected value. * @function * @name matchers#toBeLessThan * @since 2.0.0 * @param {Number} expected - The expected value to compare against. * @example * expect(result).toBeLessThan(0); */ function toBeLessThan() { return { compare: function(actual, expected) { return { pass: actual < expected }; } }; } return toBeLessThan; }; getJasmineRequireObj().toBeLessThanOrEqual = function() { /** * {@link expect} the actual value to be less than or equal to the expected value. * @function * @name matchers#toBeLessThanOrEqual * @since 2.0.0 * @param {Number} expected - The expected value to compare against. * @example * expect(result).toBeLessThanOrEqual(123); */ function toBeLessThanOrEqual() { return { compare: function(actual, expected) { return { pass: actual <= expected }; } }; } return toBeLessThanOrEqual; }; getJasmineRequireObj().toBeNaN = function(j$) { /** * {@link expect} the actual value to be `NaN` (Not a Number). * @function * @name matchers#toBeNaN * @since 1.3.0 * @example * expect(thing).toBeNaN(); */ function toBeNaN(matchersUtil) { return { compare: function(actual) { var result = { pass: (actual !== actual) }; if (result.pass) { result.message = 'Expected actual not to be NaN.'; } else { result.message = function() { return 'Expected ' + matchersUtil.pp(actual) + ' to be NaN.'; }; } return result; } }; } return toBeNaN; }; getJasmineRequireObj().toBeNegativeInfinity = function(j$) { /** * {@link expect} the actual value to be `-Infinity` (-infinity). * @function * @name matchers#toBeNegativeInfinity * @since 2.6.0 * @example * expect(thing).toBeNegativeInfinity(); */ function toBeNegativeInfinity(matchersUtil) { return { compare: function(actual) { var result = { pass: (actual === Number.NEGATIVE_INFINITY) }; if (result.pass) { result.message = 'Expected actual not to be -Infinity.'; } else { result.message = function() { return 'Expected ' + matchersUtil.pp(actual) + ' to be -Infinity.'; }; } return result; } }; } return toBeNegativeInfinity; }; getJasmineRequireObj().toBeNull = function() { /** * {@link expect} the actual value to be `null`. * @function * @name matchers#toBeNull * @since 1.3.0 * @example * expect(result).toBeNull(); */ function toBeNull() { return { compare: function(actual) { return { pass: actual === null }; } }; } return toBeNull; }; getJasmineRequireObj().toBePositiveInfinity = function(j$) { /** * {@link expect} the actual value to be `Infinity` (infinity). * @function * @name matchers#toBePositiveInfinity * @since 2.6.0 * @example * expect(thing).toBePositiveInfinity(); */ function toBePositiveInfinity(matchersUtil) { return { compare: function(actual) { var result = { pass: (actual === Number.POSITIVE_INFINITY) }; if (result.pass) { result.message = 'Expected actual not to be Infinity.'; } else { result.message = function() { return 'Expected ' + matchersUtil.pp(actual) + ' to be Infinity.'; }; } return result; } }; } return toBePositiveInfinity; }; getJasmineRequireObj().toBeTrue = function() { /** * {@link expect} the actual value to be `true`. * @function * @name matchers#toBeTrue * @since 3.5.0 * @example * expect(result).toBeTrue(); */ function toBeTrue() { return { compare: function(actual) { return { pass: actual === true }; } }; } return toBeTrue; }; getJasmineRequireObj().toBeTruthy = function() { /** * {@link expect} the actual value to be truthy. * @function * @name matchers#toBeTruthy * @since 2.0.0 * @example * expect(thing).toBeTruthy(); */ function toBeTruthy() { return { compare: function(actual) { return { pass: !!actual }; } }; } return toBeTruthy; }; getJasmineRequireObj().toBeUndefined = function() { /** * {@link expect} the actual value to be `undefined`. * @function * @name matchers#toBeUndefined * @since 1.3.0 * @example * expect(result).toBeUndefined(): */ function toBeUndefined() { return { compare: function(actual) { return { pass: void 0 === actual }; } }; } return toBeUndefined; }; getJasmineRequireObj().toContain = function() { /** * {@link expect} the actual value to contain a specific value. * @function * @name matchers#toContain * @since 2.0.0 * @param {Object} expected - The value to look for. * @example * expect(array).toContain(anElement); * expect(string).toContain(substring); */ function toContain(matchersUtil) { return { compare: function(actual, expected) { return { pass: matchersUtil.contains(actual, expected) }; } }; } return toContain; }; getJasmineRequireObj().toEqual = function(j$) { /** * {@link expect} the actual value to be equal to the expected, using deep equality comparison. * @function * @name matchers#toEqual * @since 1.3.0 * @param {Object} expected - Expected value * @example * expect(bigObject).toEqual({"foo": ['bar', 'baz']}); */ function toEqual(matchersUtil) { return { compare: function(actual, expected) { var result = { pass: false }, diffBuilder = j$.DiffBuilder({prettyPrinter: matchersUtil.pp}); result.pass = matchersUtil.equals(actual, expected, diffBuilder); // TODO: only set error message if test fails result.message = diffBuilder.getMessage(); return result; } }; } return toEqual; }; getJasmineRequireObj().toHaveBeenCalled = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalled()'); /** * {@link expect} the actual (a {@link Spy}) to have been called. * @function * @name matchers#toHaveBeenCalled * @since 1.3.0 * @example * expect(mySpy).toHaveBeenCalled(); * expect(mySpy).not.toHaveBeenCalled(); */ function toHaveBeenCalled(matchersUtil) { return { compare: function(actual) { var result = {}; if (!j$.isSpy(actual)) { throw new Error(getErrorMsg('Expected a spy, but got ' + matchersUtil.pp(actual) + '.')); } if (arguments.length > 1) { throw new Error(getErrorMsg('Does not take arguments, use toHaveBeenCalledWith')); } result.pass = actual.calls.any(); result.message = result.pass ? 'Expected spy ' + actual.and.identity + ' not to have been called.' : 'Expected spy ' + actual.and.identity + ' to have been called.'; return result; } }; } return toHaveBeenCalled; }; getJasmineRequireObj().toHaveBeenCalledBefore = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledBefore()'); /** * {@link expect} the actual value (a {@link Spy}) to have been called before another {@link Spy}. * @function * @name matchers#toHaveBeenCalledBefore * @since 2.6.0 * @param {Spy} expected - {@link Spy} that should have been called after the `actual` {@link Spy}. * @example * expect(mySpy).toHaveBeenCalledBefore(otherSpy); */ function toHaveBeenCalledBefore(matchersUtil) { return { compare: function(firstSpy, latterSpy) { if (!j$.isSpy(firstSpy)) { throw new Error(getErrorMsg('Expected a spy, but got ' + matchersUtil.pp(firstSpy) + '.')); } if (!j$.isSpy(latterSpy)) { throw new Error(getErrorMsg('Expected a spy, but got ' + matchersUtil.pp(latterSpy) + '.')); } var result = { pass: false }; if (!firstSpy.calls.count()) { result.message = 'Expected spy ' + firstSpy.and.identity + ' to have been called.'; return result; } if (!latterSpy.calls.count()) { result.message = 'Expected spy ' + latterSpy.and.identity + ' to have been called.'; return result; } var latest1stSpyCall = firstSpy.calls.mostRecent().invocationOrder; var first2ndSpyCall = latterSpy.calls.first().invocationOrder; result.pass = latest1stSpyCall < first2ndSpyCall; if (result.pass) { result.message = 'Expected spy ' + firstSpy.and.identity + ' to not have been called before spy ' + latterSpy.and.identity + ', but it was'; } else { var first1stSpyCall = firstSpy.calls.first().invocationOrder; var latest2ndSpyCall = latterSpy.calls.mostRecent().invocationOrder; if(first1stSpyCall < first2ndSpyCall) { result.message = 'Expected latest call to spy ' + firstSpy.and.identity + ' to have been called before first call to spy ' + latterSpy.and.identity + ' (no interleaved calls)'; } else if (latest2ndSpyCall > latest1stSpyCall) { result.message = 'Expected first call to spy ' + latterSpy.and.identity + ' to have been called after latest call to spy ' + firstSpy.and.identity + ' (no interleaved calls)'; } else { result.message = 'Expected spy ' + firstSpy.and.identity + ' to have been called before spy ' + latterSpy.and.identity; } } return result; } }; } return toHaveBeenCalledBefore; }; getJasmineRequireObj().toHaveBeenCalledOnceWith = function (j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledOnceWith(...arguments)'); /** * {@link expect} the actual (a {@link Spy}) to have been called exactly once, and exactly with the particular arguments. * @function * @name matchers#toHaveBeenCalledOnceWith * @since 3.6.0 * @param {...Object} - The arguments to look for * @example * expect(mySpy).toHaveBeenCalledOnceWith('foo', 'bar', 2); */ function toHaveBeenCalledOnceWith(util) { return { compare: function () { var args = Array.prototype.slice.call(arguments, 0), actual = args[0], expectedArgs = args.slice(1); if (!j$.isSpy(actual)) { throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.')); } var prettyPrintedCalls = actual.calls.allArgs().map(function (argsForCall) { return ' ' + j$.pp(argsForCall); }); if (actual.calls.count() === 1 && util.contains(actual.calls.allArgs(), expectedArgs)) { return { pass: true, message: 'Expected spy ' + actual.and.identity + ' to have been called 0 times, multiple times, or once, but with arguments different from:\n' + ' ' + j$.pp(expectedArgs) + '\n' + 'But the actual call was:\n' + prettyPrintedCalls.join(',\n') + '.\n\n' }; } function getDiffs() { return actual.calls.allArgs().map(function (argsForCall, callIx) { var diffBuilder = new j$.DiffBuilder(); util.equals(argsForCall, expectedArgs, diffBuilder); return diffBuilder.getMessage(); }); } function butString() { switch (actual.calls.count()) { case 0: return 'But it was never called.\n\n'; case 1: return 'But the actual call was:\n' + prettyPrintedCalls.join(',\n') + '.\n' + getDiffs().join('\n') + '\n\n'; default: return 'But the actual calls were:\n' + prettyPrintedCalls.join(',\n') + '.\n\n'; } } return { pass: false, message: 'Expected spy ' + actual.and.identity + ' to have been called only once, and with given args:\n' + ' ' + j$.pp(expectedArgs) + '\n' + butString() }; } }; } return toHaveBeenCalledOnceWith; }; getJasmineRequireObj().toHaveBeenCalledTimes = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledTimes()'); /** * {@link expect} the actual (a {@link Spy}) to have been called the specified number of times. * @function * @name matchers#toHaveBeenCalledTimes * @since 2.4.0 * @param {Number} expected - The number of invocations to look for. * @example * expect(mySpy).toHaveBeenCalledTimes(3); */ function toHaveBeenCalledTimes(matchersUtil) { return { compare: function(actual, expected) { if (!j$.isSpy(actual)) { throw new Error(getErrorMsg('Expected a spy, but got ' + matchersUtil.pp(actual) + '.')); } var args = Array.prototype.slice.call(arguments, 0), result = { pass: false }; if (!j$.isNumber_(expected)) { throw new Error(getErrorMsg('The expected times failed is a required argument and must be a number.')); } actual = args[0]; var calls = actual.calls.count(); var timesMessage = expected === 1 ? 'once' : expected + ' times'; result.pass = calls === expected; result.message = result.pass ? 'Expected spy ' + actual.and.identity + ' not to have been called ' + timesMessage + '. It was called ' + calls + ' times.' : 'Expected spy ' + actual.and.identity + ' to have been called ' + timesMessage + '. It was called ' + calls + ' times.'; return result; } }; } return toHaveBeenCalledTimes; }; getJasmineRequireObj().toHaveBeenCalledWith = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledWith(...arguments)'); /** * {@link expect} the actual (a {@link Spy}) to have been called with particular arguments at least once. * @function * @name matchers#toHaveBeenCalledWith * @since 1.3.0 * @param {...Object} - The arguments to look for * @example * expect(mySpy).toHaveBeenCalledWith('foo', 'bar', 2); */ function toHaveBeenCalledWith(matchersUtil) { return { compare: function() { var args = Array.prototype.slice.call(arguments, 0), actual = args[0], expectedArgs = args.slice(1), result = { pass: false }; if (!j$.isSpy(actual)) { throw new Error(getErrorMsg('Expected a spy, but got ' + matchersUtil.pp(actual) + '.')); } if (!actual.calls.any()) { result.message = function() { return 'Expected spy ' + actual.and.identity + ' to have been called with:\n' + ' ' + matchersUtil.pp(expectedArgs) + '\nbut it was never called.'; }; return result; } if (matchersUtil.contains(actual.calls.allArgs(), expectedArgs)) { result.pass = true; result.message = function() { return 'Expected spy ' + actual.and.identity + ' not to have been called with:\n' + ' ' + matchersUtil.pp(expectedArgs) + '\nbut it was.'; }; } else { result.message = function() { var prettyPrintedCalls = actual.calls.allArgs().map(function(argsForCall) { return ' ' + matchersUtil.pp(argsForCall); }); var diffs = actual.calls.allArgs().map(function(argsForCall, callIx) { var diffBuilder = new j$.DiffBuilder(); matchersUtil.equals(argsForCall, expectedArgs, diffBuilder); return 'Call ' + callIx + ':\n' + diffBuilder.getMessage().replace(/^/mg, ' '); }); return 'Expected spy ' + actual.and.identity + ' to have been called with:\n' + ' ' + matchersUtil.pp(expectedArgs) + '\n' + '' + 'but actual calls were:\n' + prettyPrintedCalls.join(',\n') + '.\n\n' + diffs.join('\n'); }; } return result; } }; } return toHaveBeenCalledWith; }; getJasmineRequireObj().toHaveClass = function(j$) { /** * {@link expect} the actual value to be a DOM element that has the expected class * @function * @name matchers#toHaveClass * @since 3.0.0 * @param {Object} expected - The class name to test for * @example * var el = document.createElement('div'); * el.className = 'foo bar baz'; * expect(el).toHaveClass('bar'); */ function toHaveClass(matchersUtil) { return { compare: function(actual, expected) { if (!isElement(actual)) { throw new Error(matchersUtil.pp(actual) + ' is not a DOM element'); } return { pass: actual.classList.contains(expected) }; } }; } function isElement(maybeEl) { return maybeEl && maybeEl.classList && j$.isFunction_(maybeEl.classList.contains); } return toHaveClass; }; getJasmineRequireObj().toHaveSize = function(j$) { /** * {@link expect} the actual size to be equal to the expected, using array-like length or object keys size. * @function * @name matchers#toHaveSize * @since 3.6.0 * @param {Object} expected - Expected size * @example * array = [1,2]; * expect(array).toHaveSize(2); */ function toHaveSize() { return { compare: function(actual, expected) { var result = { pass: false }; if (j$.isA_('WeakSet', actual) || j$.isWeakMap(actual) || j$.isDataView(actual)) { throw new Error('Cannot get size of ' + actual + '.'); } if (j$.isSet(actual) || j$.isMap(actual)) { result.pass = actual.size === expected; } else if (isLength(actual.length)) { result.pass = actual.length === expected; } else { result.pass = Object.keys(actual).length === expected; } return result; } }; } var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; // eslint-disable-line compat/compat function isLength(value) { return (typeof value == 'number') && value > -1 && value % 1 === 0 && value <= MAX_SAFE_INTEGER; } return toHaveSize; }; getJasmineRequireObj().toMatch = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toMatch( || )'); /** * {@link expect} the actual value to match a regular expression * @function * @name matchers#toMatch * @since 1.3.0 * @param {RegExp|String} expected - Value to look for in the string. * @example * expect("my string").toMatch(/string$/); * expect("other string").toMatch("her"); */ function toMatch() { return { compare: function(actual, expected) { if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { throw new Error(getErrorMsg('Expected is not a String or a RegExp')); } var regexp = new RegExp(expected); return { pass: regexp.test(actual) }; } }; } return toMatch; }; getJasmineRequireObj().toThrow = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect(function() {}).toThrow()'); /** * {@link expect} a function to `throw` something. * @function * @name matchers#toThrow * @since 2.0.0 * @param {Object} [expected] - Value that should be thrown. If not provided, simply the fact that something was thrown will be checked. * @example * expect(function() { return 'things'; }).toThrow('foo'); * expect(function() { return 'stuff'; }).toThrow(); */ function toThrow(matchersUtil) { return { compare: function(actual, expected) { var result = { pass: false }, threw = false, thrown; if (typeof actual != 'function') { throw new Error(getErrorMsg('Actual is not a Function')); } try { actual(); } catch (e) { threw = true; thrown = e; } if (!threw) { result.message = 'Expected function to throw an exception.'; return result; } if (arguments.length == 1) { result.pass = true; result.message = function() { return 'Expected function not to throw, but it threw ' + matchersUtil.pp(thrown) + '.'; }; return result; } if (matchersUtil.equals(thrown, expected)) { result.pass = true; result.message = function() { return 'Expected function not to throw ' + matchersUtil.pp(expected) + '.'; }; } else { result.message = function() { return 'Expected function to throw ' + matchersUtil.pp(expected) + ', but it threw ' + matchersUtil.pp(thrown) + '.'; }; } return result; } }; } return toThrow; }; getJasmineRequireObj().toThrowError = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect(function() {}).toThrowError(, )'); /** * {@link expect} a function to `throw` an `Error`. * @function * @name matchers#toThrowError * @since 2.0.0 * @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used. * @param {RegExp|String} [message] - The message that should be set on the thrown `Error` * @example * expect(function() { return 'things'; }).toThrowError(MyCustomError, 'message'); * expect(function() { return 'things'; }).toThrowError(MyCustomError, /bar/); * expect(function() { return 'stuff'; }).toThrowError(MyCustomError); * expect(function() { return 'other'; }).toThrowError(/foo/); * expect(function() { return 'other'; }).toThrowError(); */ function toThrowError(matchersUtil) { return { compare: function(actual) { var errorMatcher = getMatcher.apply(null, arguments), thrown; if (typeof actual != 'function') { throw new Error(getErrorMsg('Actual is not a Function')); } try { actual(); return fail('Expected function to throw an Error.'); } catch (e) { thrown = e; } if (!j$.isError_(thrown)) { return fail(function() { return 'Expected function to throw an Error, but it threw ' + matchersUtil.pp(thrown) + '.'; }); } return errorMatcher.match(thrown); } }; function getMatcher() { var expected, errorType; if (arguments[2]) { errorType = arguments[1]; expected = arguments[2]; if (!isAnErrorType(errorType)) { throw new Error(getErrorMsg('Expected error type is not an Error.')); } return exactMatcher(expected, errorType); } else if (arguments[1]) { expected = arguments[1]; if (isAnErrorType(arguments[1])) { return exactMatcher(null, arguments[1]); } else { return exactMatcher(arguments[1], null); } } else { return anyMatcher(); } } function anyMatcher() { return { match: function(error) { return pass('Expected function not to throw an Error, but it threw ' + j$.fnNameFor(error) + '.'); } }; } function exactMatcher(expected, errorType) { if (expected && !isStringOrRegExp(expected)) { if (errorType) { throw new Error(getErrorMsg('Expected error message is not a string or RegExp.')); } else { throw new Error(getErrorMsg('Expected is not an Error, string, or RegExp.')); } } function messageMatch(message) { if (typeof expected == 'string') { return expected == message; } else { return expected.test(message); } } var errorTypeDescription = errorType ? j$.fnNameFor(errorType) : 'an exception'; function thrownDescription(thrown) { var thrownName = errorType ? j$.fnNameFor(thrown.constructor) : 'an exception', thrownMessage = ''; if (expected) { thrownMessage = ' with message ' + matchersUtil.pp(thrown.message); } return thrownName + thrownMessage; } function messageDescription() { if (expected === null) { return ''; } else if (expected instanceof RegExp) { return ' with a message matching ' + matchersUtil.pp(expected); } else { return ' with message ' + matchersUtil.pp(expected); } } function matches(error) { return (errorType === null || error instanceof errorType) && (expected === null || messageMatch(error.message)); } return { match: function(thrown) { if (matches(thrown)) { return pass(function() { return 'Expected function not to throw ' + errorTypeDescription + messageDescription() + '.'; }); } else { return fail(function() { return 'Expected function to throw ' + errorTypeDescription + messageDescription() + ', but it threw ' + thrownDescription(thrown) + '.'; }); } } }; } function isStringOrRegExp(potential) { return potential instanceof RegExp || (typeof potential == 'string'); } function isAnErrorType(type) { if (typeof type !== 'function') { return false; } var Surrogate = function() {}; Surrogate.prototype = type.prototype; return j$.isError_(new Surrogate()); } } function pass(message) { return { pass: true, message: message }; } function fail(message) { return { pass: false, message: message }; } return toThrowError; }; getJasmineRequireObj().toThrowMatching = function(j$) { var usageError = j$.formatErrorMsg('', 'expect(function() {}).toThrowMatching()'); /** * {@link expect} a function to `throw` something matching a predicate. * @function * @name matchers#toThrowMatching * @since 3.0.0 * @param {Function} predicate - A function that takes the thrown exception as its parameter and returns true if it matches. * @example * expect(function() { throw new Error('nope'); }).toThrowMatching(function(thrown) { return thrown.message === 'nope'; }); */ function toThrowMatching(matchersUtil) { return { compare: function(actual, predicate) { var thrown; if (typeof actual !== 'function') { throw new Error(usageError('Actual is not a Function')); } if (typeof predicate !== 'function') { throw new Error(usageError('Predicate is not a Function')); } try { actual(); return fail('Expected function to throw an exception.'); } catch (e) { thrown = e; } if (predicate(thrown)) { return pass('Expected function not to throw an exception matching a predicate.'); } else { return fail(function() { return 'Expected function to throw an exception matching a predicate, ' + 'but it threw ' + thrownDescription(thrown) + '.'; }); } } }; function thrownDescription(thrown) { if (thrown && thrown.constructor) { return j$.fnNameFor(thrown.constructor) + ' with message ' + matchersUtil.pp(thrown.message); } else { return matchersUtil.pp(thrown); } } } function pass(message) { return { pass: true, message: message }; } function fail(message) { return { pass: false, message: message }; } return toThrowMatching; }; getJasmineRequireObj().MockDate = function() { function MockDate(global) { var self = this; var currentTime = 0; if (!global || !global.Date) { self.install = function() {}; self.tick = function() {}; self.uninstall = function() {}; return self; } var GlobalDate = global.Date; self.install = function(mockDate) { if (mockDate instanceof GlobalDate) { currentTime = mockDate.getTime(); } else { currentTime = new GlobalDate().getTime(); } global.Date = FakeDate; }; self.tick = function(millis) { millis = millis || 0; currentTime = currentTime + millis; }; self.uninstall = function() { currentTime = 0; global.Date = GlobalDate; }; createDateProperties(); return self; function FakeDate() { switch (arguments.length) { case 0: return new GlobalDate(currentTime); case 1: return new GlobalDate(arguments[0]); case 2: return new GlobalDate(arguments[0], arguments[1]); case 3: return new GlobalDate(arguments[0], arguments[1], arguments[2]); case 4: return new GlobalDate( arguments[0], arguments[1], arguments[2], arguments[3] ); case 5: return new GlobalDate( arguments[0], arguments[1], arguments[2], arguments[3], arguments[4] ); case 6: return new GlobalDate( arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5] ); default: return new GlobalDate( arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6] ); } } function createDateProperties() { FakeDate.prototype = GlobalDate.prototype; FakeDate.now = function() { if (GlobalDate.now) { return currentTime; } else { throw new Error('Browser does not support Date.now()'); } }; FakeDate.toSource = GlobalDate.toSource; FakeDate.toString = GlobalDate.toString; FakeDate.parse = GlobalDate.parse; FakeDate.UTC = GlobalDate.UTC; } } return MockDate; }; getJasmineRequireObj().makePrettyPrinter = function(j$) { function SinglePrettyPrintRun(customObjectFormatters, pp) { this.customObjectFormatters_ = customObjectFormatters; this.ppNestLevel_ = 0; this.seen = []; this.length = 0; this.stringParts = []; this.pp_ = pp; } function hasCustomToString(value) { // value.toString !== Object.prototype.toString if value has no custom toString but is from another context (e.g. // iframe, web worker) try { return ( j$.isFunction_(value.toString) && value.toString !== Object.prototype.toString && value.toString() !== Object.prototype.toString.call(value) ); } catch (e) { // The custom toString() threw. return true; } } SinglePrettyPrintRun.prototype.format = function(value) { this.ppNestLevel_++; try { var customFormatResult = this.applyCustomFormatters_(value); if (customFormatResult) { this.emitScalar(customFormatResult); } else if (j$.util.isUndefined(value)) { this.emitScalar('undefined'); } else if (value === null) { this.emitScalar('null'); } else if (value === 0 && 1 / value === -Infinity) { this.emitScalar('-0'); } else if (value === j$.getGlobal()) { this.emitScalar(''); } else if (value.jasmineToString) { this.emitScalar(value.jasmineToString(this.pp_)); } else if (typeof value === 'string') { this.emitString(value); } else if (j$.isSpy(value)) { this.emitScalar('spy on ' + value.and.identity); } else if (j$.isSpy(value.toString)) { this.emitScalar('spy on ' + value.toString.and.identity); } else if (value instanceof RegExp) { this.emitScalar(value.toString()); } else if (typeof value === 'function') { this.emitScalar('Function'); } else if (j$.isDomNode(value)) { if (value.tagName) { this.emitDomElement(value); } else { this.emitScalar('HTMLNode'); } } else if (value instanceof Date) { this.emitScalar('Date(' + value + ')'); } else if (j$.isSet(value)) { this.emitSet(value); } else if (j$.isMap(value)) { this.emitMap(value); } else if (j$.isTypedArray_(value)) { this.emitTypedArray(value); } else if ( value.toString && typeof value === 'object' && !j$.isArray_(value) && hasCustomToString(value) ) { try { this.emitScalar(value.toString()); } catch (e) { this.emitScalar('has-invalid-toString-method'); } } else if (j$.util.arrayContains(this.seen, value)) { this.emitScalar( '' ); } else if (j$.isArray_(value) || j$.isA_('Object', value)) { this.seen.push(value); if (j$.isArray_(value)) { this.emitArray(value); } else { this.emitObject(value); } this.seen.pop(); } else { this.emitScalar(value.toString()); } } catch (e) { if (this.ppNestLevel_ > 1 || !(e instanceof MaxCharsReachedError)) { throw e; } } finally { this.ppNestLevel_--; } }; SinglePrettyPrintRun.prototype.applyCustomFormatters_ = function(value) { return customFormat(value, this.customObjectFormatters_); }; SinglePrettyPrintRun.prototype.iterateObject = function(obj, fn) { var objKeys = keys(obj, j$.isArray_(obj)); var isGetter = function isGetter(prop) {}; if (obj.__lookupGetter__) { isGetter = function isGetter(prop) { var getter = obj.__lookupGetter__(prop); return !j$.util.isUndefined(getter) && getter !== null; }; } var length = Math.min(objKeys.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); for (var i = 0; i < length; i++) { var property = objKeys[i]; fn(property, isGetter(property)); } return objKeys.length > length; }; SinglePrettyPrintRun.prototype.emitScalar = function(value) { this.append(value); }; SinglePrettyPrintRun.prototype.emitString = function(value) { this.append("'" + value + "'"); }; SinglePrettyPrintRun.prototype.emitArray = function(array) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Array'); return; } var length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); this.append('[ '); for (var i = 0; i < length; i++) { if (i > 0) { this.append(', '); } this.format(array[i]); } if (array.length > length) { this.append(', ...'); } var self = this; var first = array.length === 0; var truncated = this.iterateObject(array, function(property, isGetter) { if (first) { first = false; } else { self.append(', '); } self.formatProperty(array, property, isGetter); }); if (truncated) { this.append(', ...'); } this.append(' ]'); }; SinglePrettyPrintRun.prototype.emitSet = function(set) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Set'); return; } this.append('Set( '); var size = Math.min(set.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); var i = 0; set.forEach(function(value, key) { if (i >= size) { return; } if (i > 0) { this.append(', '); } this.format(value); i++; }, this); if (set.size > size) { this.append(', ...'); } this.append(' )'); }; SinglePrettyPrintRun.prototype.emitMap = function(map) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Map'); return; } this.append('Map( '); var size = Math.min(map.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); var i = 0; map.forEach(function(value, key) { if (i >= size) { return; } if (i > 0) { this.append(', '); } this.format([key, value]); i++; }, this); if (map.size > size) { this.append(', ...'); } this.append(' )'); }; SinglePrettyPrintRun.prototype.emitObject = function(obj) { var ctor = obj.constructor, constructorName; constructorName = typeof ctor === 'function' && obj instanceof ctor ? j$.fnNameFor(obj.constructor) : 'null'; this.append(constructorName); if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { return; } var self = this; this.append('({ '); var first = true; var truncated = this.iterateObject(obj, function(property, isGetter) { if (first) { first = false; } else { self.append(', '); } self.formatProperty(obj, property, isGetter); }); if (truncated) { this.append(', ...'); } this.append(' })'); }; SinglePrettyPrintRun.prototype.emitTypedArray = function(arr) { var constructorName = j$.fnNameFor(arr.constructor), limitedArray = Array.prototype.slice.call( arr, 0, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH ), itemsString = Array.prototype.join.call(limitedArray, ', '); if (limitedArray.length !== arr.length) { itemsString += ', ...'; } this.append(constructorName + ' [ ' + itemsString + ' ]'); }; SinglePrettyPrintRun.prototype.emitDomElement = function(el) { var tagName = el.tagName.toLowerCase(), attrs = el.attributes, i, len = attrs.length, out = '<' + tagName, attr; for (i = 0; i < len; i++) { attr = attrs[i]; out += ' ' + attr.name; if (attr.value !== '') { out += '="' + attr.value + '"'; } } out += '>'; if (el.childElementCount !== 0 || el.textContent !== '') { out += '...'; } this.append(out); }; SinglePrettyPrintRun.prototype.formatProperty = function( obj, property, isGetter ) { this.append(property); this.append(': '); if (isGetter) { this.append(''); } else { this.format(obj[property]); } }; SinglePrettyPrintRun.prototype.append = function(value) { // This check protects us from the rare case where an object has overriden // `toString()` with an invalid implementation (returning a non-string). if (typeof value !== 'string') { value = Object.prototype.toString.call(value); } var result = truncate(value, j$.MAX_PRETTY_PRINT_CHARS - this.length); this.length += result.value.length; this.stringParts.push(result.value); if (result.truncated) { throw new MaxCharsReachedError(); } }; function truncate(s, maxlen) { if (s.length <= maxlen) { return { value: s, truncated: false }; } s = s.substring(0, maxlen - 4) + ' ...'; return { value: s, truncated: true }; } function MaxCharsReachedError() { this.message = 'Exceeded ' + j$.MAX_PRETTY_PRINT_CHARS + ' characters while pretty-printing a value'; } MaxCharsReachedError.prototype = new Error(); function keys(obj, isArray) { var allKeys = Object.keys ? Object.keys(obj) : (function(o) { var keys = []; for (var key in o) { if (j$.util.has(o, key)) { keys.push(key); } } return keys; })(obj); if (!isArray) { return allKeys; } if (allKeys.length === 0) { return allKeys; } var extraKeys = []; for (var i = 0; i < allKeys.length; i++) { if (!/^[0-9]+$/.test(allKeys[i])) { extraKeys.push(allKeys[i]); } } return extraKeys; } function customFormat(value, customObjectFormatters) { var i, result; for (i = 0; i < customObjectFormatters.length; i++) { result = customObjectFormatters[i](value); if (result !== undefined) { return result; } } } return function(customObjectFormatters) { customObjectFormatters = customObjectFormatters || []; var pp = function(value) { var prettyPrinter = new SinglePrettyPrintRun(customObjectFormatters, pp); prettyPrinter.format(value); return prettyPrinter.stringParts.join(''); }; pp.customFormat_ = function(value) { return customFormat(value, customObjectFormatters); }; return pp; }; }; getJasmineRequireObj().QueueRunner = function(j$) { function StopExecutionError() {} StopExecutionError.prototype = new Error(); j$.StopExecutionError = StopExecutionError; function once(fn) { var called = false; return function(arg) { if (!called) { called = true; // Direct call using single parameter, because cleanup/next does not need more fn(arg); } return null; }; } function emptyFn() {} function QueueRunner(attrs) { var queueableFns = attrs.queueableFns || []; this.queueableFns = queueableFns.concat(attrs.cleanupFns || []); this.firstCleanupIx = queueableFns.length; this.onComplete = attrs.onComplete || emptyFn; this.clearStack = attrs.clearStack || function(fn) { fn(); }; this.onException = attrs.onException || emptyFn; this.userContext = attrs.userContext || new j$.UserContext(); this.timeout = attrs.timeout || { setTimeout: setTimeout, clearTimeout: clearTimeout }; this.fail = attrs.fail || emptyFn; this.globalErrors = attrs.globalErrors || { pushListener: emptyFn, popListener: emptyFn }; this.completeOnFirstError = !!attrs.completeOnFirstError; this.errored = false; if (typeof this.onComplete !== 'function') { throw new Error('invalid onComplete ' + JSON.stringify(this.onComplete)); } this.deprecated = attrs.deprecated; } QueueRunner.prototype.execute = function() { var self = this; this.handleFinalError = function(message, source, lineno, colno, error) { // Older browsers would send the error as the first parameter. HTML5 // specifies the the five parameters above. The error instance should // be preffered, otherwise the call stack would get lost. self.onException(error || message); }; this.globalErrors.pushListener(this.handleFinalError); this.run(0); }; QueueRunner.prototype.skipToCleanup = function(lastRanIndex) { if (lastRanIndex < this.firstCleanupIx) { this.run(this.firstCleanupIx); } else { this.run(lastRanIndex + 1); } }; QueueRunner.prototype.clearTimeout = function(timeoutId) { Function.prototype.apply.apply(this.timeout.clearTimeout, [ j$.getGlobal(), [timeoutId] ]); }; QueueRunner.prototype.setTimeout = function(fn, timeout) { return Function.prototype.apply.apply(this.timeout.setTimeout, [ j$.getGlobal(), [fn, timeout] ]); }; QueueRunner.prototype.attempt = function attempt(iterativeIndex) { var self = this, completedSynchronously = true, handleError = function handleError(error) { onException(error); next(error); }, cleanup = once(function cleanup() { if (timeoutId !== void 0) { self.clearTimeout(timeoutId); } self.globalErrors.popListener(handleError); }), next = once(function next(err) { cleanup(); if (j$.isError_(err)) { if (!(err instanceof StopExecutionError) && !err.jasmineMessage) { self.fail(err); } self.errored = errored = true; } function runNext() { if (self.completeOnFirstError && errored) { self.skipToCleanup(iterativeIndex); } else { self.run(iterativeIndex + 1); } } if (completedSynchronously) { self.setTimeout(runNext); } else { runNext(); } }), errored = false, queueableFn = self.queueableFns[iterativeIndex], timeoutId; next.fail = function nextFail() { self.fail.apply(null, arguments); self.errored = errored = true; next(); }; self.globalErrors.pushListener(handleError); if (queueableFn.timeout !== undefined) { var timeoutInterval = queueableFn.timeout || j$.DEFAULT_TIMEOUT_INTERVAL; timeoutId = self.setTimeout(function() { var error = new Error( 'Timeout - Async function did not complete within ' + timeoutInterval + 'ms ' + (queueableFn.timeout ? '(custom timeout)' : '(set by jasmine.DEFAULT_TIMEOUT_INTERVAL)') ); onException(error); next(); }, timeoutInterval); } try { if (queueableFn.fn.length === 0) { var maybeThenable = queueableFn.fn.call(self.userContext); if (maybeThenable && j$.isFunction_(maybeThenable.then)) { maybeThenable.then(next, onPromiseRejection); completedSynchronously = false; return { completedSynchronously: false }; } } else { queueableFn.fn.call(self.userContext, next); completedSynchronously = false; return { completedSynchronously: false }; } } catch (e) { onException(e); self.errored = errored = true; } cleanup(); return { completedSynchronously: true, errored: errored }; function onException(e) { self.onException(e); self.errored = errored = true; } function onPromiseRejection(e) { onException(e); next(); } }; QueueRunner.prototype.run = function(recursiveIndex) { var length = this.queueableFns.length, self = this, iterativeIndex; for ( iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++ ) { var result = this.attempt(iterativeIndex); if (!result.completedSynchronously) { return; } self.errored = self.errored || result.errored; if (this.completeOnFirstError && result.errored) { this.skipToCleanup(iterativeIndex); return; } } this.clearStack(function() { self.globalErrors.popListener(self.handleFinalError); self.onComplete(self.errored && new StopExecutionError()); }); }; return QueueRunner; }; getJasmineRequireObj().ReportDispatcher = function(j$) { function ReportDispatcher(methods, queueRunnerFactory) { var dispatchedMethods = methods || []; for (var i = 0; i < dispatchedMethods.length; i++) { var method = dispatchedMethods[i]; this[method] = (function(m) { return function() { dispatch(m, arguments); }; })(method); } var reporters = []; var fallbackReporter = null; this.addReporter = function(reporter) { reporters.push(reporter); }; this.provideFallbackReporter = function(reporter) { fallbackReporter = reporter; }; this.clearReporters = function() { reporters = []; }; return this; function dispatch(method, args) { if (reporters.length === 0 && fallbackReporter !== null) { reporters.push(fallbackReporter); } var onComplete = args[args.length - 1]; args = j$.util.argsToArray(args).splice(0, args.length - 1); var fns = []; for (var i = 0; i < reporters.length; i++) { var reporter = reporters[i]; addFn(fns, reporter, method, args); } queueRunnerFactory({ queueableFns: fns, onComplete: onComplete, isReporter: true }); } function addFn(fns, reporter, method, args) { var fn = reporter[method]; if (!fn) { return; } var thisArgs = j$.util.cloneArgs(args); if (fn.length <= 1) { fns.push({ fn: function() { return fn.apply(reporter, thisArgs); } }); } else { fns.push({ fn: function(done) { return fn.apply(reporter, thisArgs.concat([done])); } }); } } } return ReportDispatcher; }; getJasmineRequireObj().interface = function(jasmine, env) { var jasmineInterface = { /** * Callback passed to parts of the Jasmine base interface. * * By default Jasmine assumes this function completes synchronously. * If you have code that you need to test asynchronously, you can declare that you receive a `done` callback, return a Promise, or use the `async` keyword if it is supported in your environment. * @callback implementationCallback * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. */ /** * Create a group of specs (often called a suite). * * Calls to `describe` can be nested within other calls to compose your suite as a tree. * @name describe * @since 1.3.0 * @function * @global * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ describe: function(description, specDefinitions) { return env.describe(description, specDefinitions); }, /** * A temporarily disabled [`describe`]{@link describe} * * Specs within an `xdescribe` will be marked pending and not executed * @name xdescribe * @since 1.3.0 * @function * @global * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ xdescribe: function(description, specDefinitions) { return env.xdescribe(description, specDefinitions); }, /** * A focused [`describe`]{@link describe} * * If suites or specs are focused, only those that are focused will be executed * @see fit * @name fdescribe * @since 2.1.0 * @function * @global * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ fdescribe: function(description, specDefinitions) { return env.fdescribe(description, specDefinitions); }, /** * Define a single spec. A spec should contain one or more {@link expect|expectations} that test the state of the code. * * A spec whose expectations all succeed will be passing and a spec with any failures will fail. * The name `it` is a pronoun for the test target, not an abbreviation of anything. It makes the * spec more readable by connecting the function name `it` and the argument `description` as a * complete sentence. * @name it * @since 1.3.0 * @function * @global * @param {String} description Textual description of what this spec is checking * @param {implementationCallback} [testFunction] Function that contains the code of your test. If not provided the test will be `pending`. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. * @see async */ it: function() { return env.it.apply(env, arguments); }, /** * A temporarily disabled [`it`]{@link it} * * The spec will report as `pending` and will not be executed. * @name xit * @since 1.3.0 * @function * @global * @param {String} description Textual description of what this spec is checking. * @param {implementationCallback} [testFunction] Function that contains the code of your test. Will not be executed. */ xit: function() { return env.xit.apply(env, arguments); }, /** * A focused [`it`]{@link it} * * If suites or specs are focused, only those that are focused will be executed. * @name fit * @since 2.1.0 * @function * @global * @param {String} description Textual description of what this spec is checking. * @param {implementationCallback} testFunction Function that contains the code of your test. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. * @see async */ fit: function() { return env.fit.apply(env, arguments); }, /** * Run some shared setup before each of the specs in the {@link describe} in which it is called. * @name beforeEach * @since 1.3.0 * @function * @global * @param {implementationCallback} [function] Function that contains the code to setup your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeEach. * @see async */ beforeEach: function() { return env.beforeEach.apply(env, arguments); }, /** * Run some shared teardown after each of the specs in the {@link describe} in which it is called. * @name afterEach * @since 1.3.0 * @function * @global * @param {implementationCallback} [function] Function that contains the code to teardown your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterEach. * @see async */ afterEach: function() { return env.afterEach.apply(env, arguments); }, /** * Run some shared setup once before all of the specs in the {@link describe} are run. * * _Note:_ Be careful, sharing the setup from a beforeAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail. * @name beforeAll * @since 2.1.0 * @function * @global * @param {implementationCallback} [function] Function that contains the code to setup your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeAll. * @see async */ beforeAll: function() { return env.beforeAll.apply(env, arguments); }, /** * Run some shared teardown once after all of the specs in the {@link describe} are run. * * _Note:_ Be careful, sharing the teardown from a afterAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail. * @name afterAll * @since 2.1.0 * @function * @global * @param {implementationCallback} [function] Function that contains the code to teardown your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterAll. * @see async */ afterAll: function() { return env.afterAll.apply(env, arguments); }, /** * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult} * @name setSpecProperty * @since 3.6.0 * @function * @param {String} key The name of the property * @param {*} value The value of the property */ setSpecProperty: function(key, value) { return env.setSpecProperty(key, value); }, /** * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult} * @name setSuiteProperty * @since 3.6.0 * @function * @param {String} key The name of the property * @param {*} value The value of the property */ setSuiteProperty: function(key, value) { return env.setSuiteProperty(key, value); }, /** * Create an expectation for a spec. * @name expect * @since 1.3.0 * @function * @global * @param {Object} actual - Actual computed value to test expectations against. * @return {matchers} */ expect: function(actual) { return env.expect(actual); }, /** * Create an asynchronous expectation for a spec. Note that the matchers * that are provided by an asynchronous expectation all return promises * which must be either returned from the spec or waited for using `await` * in order for Jasmine to associate them with the correct spec. * @name expectAsync * @since 3.3.0 * @function * @global * @param {Object} actual - Actual computed value to test expectations against. * @return {async-matchers} * @example * await expectAsync(somePromise).toBeResolved(); * @example * return expectAsync(somePromise).toBeResolved(); */ expectAsync: function(actual) { return env.expectAsync(actual); }, /** * Mark a spec as pending, expectation results will be ignored. * @name pending * @since 2.0.0 * @function * @global * @param {String} [message] - Reason the spec is pending. */ pending: function() { return env.pending.apply(env, arguments); }, /** * Explicitly mark a spec as failed. * @name fail * @since 2.1.0 * @function * @global * @param {String|Error} [error] - Reason for the failure. */ fail: function() { return env.fail.apply(env, arguments); }, /** * Install a spy onto an existing object. * @name spyOn * @since 1.3.0 * @function * @global * @param {Object} obj - The object upon which to install the {@link Spy}. * @param {String} methodName - The name of the method to replace with a {@link Spy}. * @returns {Spy} */ spyOn: function(obj, methodName) { return env.spyOn(obj, methodName); }, /** * Install a spy on a property installed with `Object.defineProperty` onto an existing object. * @name spyOnProperty * @since 2.6.0 * @function * @global * @param {Object} obj - The object upon which to install the {@link Spy} * @param {String} propertyName - The name of the property to replace with a {@link Spy}. * @param {String} [accessType=get] - The access type (get|set) of the property to {@link Spy} on. * @returns {Spy} */ spyOnProperty: function(obj, methodName, accessType) { return env.spyOnProperty(obj, methodName, accessType); }, /** * Installs spies on all writable and configurable properties of an object. * @name spyOnAllFunctions * @since 3.2.1 * @function * @global * @param {Object} obj - The object upon which to install the {@link Spy}s * @returns {Object} the spied object */ spyOnAllFunctions: function(obj) { return env.spyOnAllFunctions(obj); }, jsApiReporter: new jasmine.JsApiReporter({ timer: new jasmine.Timer() }), /** * @namespace jasmine */ jasmine: jasmine }; /** * Add a custom equality tester for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addCustomEqualityTester * @since 2.0.0 * @function * @param {Function} tester - A function which takes two arguments to compare and returns a `true` or `false` comparison result if it knows how to compare them, and `undefined` otherwise. * @see custom_equality */ jasmine.addCustomEqualityTester = function(tester) { env.addCustomEqualityTester(tester); }; /** * Add custom matchers for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addMatchers * @since 2.0.0 * @function * @param {Object} matchers - Keys from this object will be the new matcher names. * @see custom_matcher */ jasmine.addMatchers = function(matchers) { return env.addMatchers(matchers); }; /** * Add custom async matchers for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addAsyncMatchers * @since 3.5.0 * @function * @param {Object} matchers - Keys from this object will be the new async matcher names. * @see custom_matcher */ jasmine.addAsyncMatchers = function(matchers) { return env.addAsyncMatchers(matchers); }; /** * Add a custom object formatter for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addCustomObjectFormatter * @since 3.6.0 * @function * @param {Function} formatter - A function which takes a value to format and returns a string if it knows how to format it, and `undefined` otherwise. * @see custom_object_formatter */ jasmine.addCustomObjectFormatter = function(formatter) { return env.addCustomObjectFormatter(formatter); }; /** * Get the currently booted mock {Clock} for this Jasmine environment. * @name jasmine.clock * @since 2.0.0 * @function * @returns {Clock} */ jasmine.clock = function() { return env.clock; }; /** * Create a bare {@link Spy} object. This won't be installed anywhere and will not have any implementation behind it. * @name jasmine.createSpy * @since 1.3.0 * @function * @param {String} [name] - Name to give the spy. This will be displayed in failure messages. * @param {Function} [originalFn] - Function to act as the real implementation. * @return {Spy} */ jasmine.createSpy = function(name, originalFn) { return env.createSpy(name, originalFn); }; /** * Create an object with multiple {@link Spy}s as its members. * @name jasmine.createSpyObj * @since 1.3.0 * @function * @param {String} [baseName] - Base name for the spies in the object. * @param {String[]|Object} methodNames - Array of method names to create spies for, or Object whose keys will be method names and values the {@link Spy#and#returnValue|returnValue}. * @param {String[]|Object} [propertyNames] - Array of property names to create spies for, or Object whose keys will be propertynames and values the {@link Spy#and#returnValue|returnValue}. * @return {Object} */ jasmine.createSpyObj = function(baseName, methodNames, propertyNames) { return env.createSpyObj(baseName, methodNames, propertyNames); }; /** * Add a custom spy strategy for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addSpyStrategy * @since 3.5.0 * @function * @param {String} name - The name of the strategy (i.e. what you call from `and`) * @param {Function} factory - Factory function that returns the plan to be executed. */ jasmine.addSpyStrategy = function(name, factory) { return env.addSpyStrategy(name, factory); }; /** * Set the default spy strategy for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.setDefaultSpyStrategy * @function * @param {Function} defaultStrategyFn - a function that assigns a strategy * @example * beforeEach(function() { * jasmine.setDefaultSpyStrategy(and => and.returnValue(true)); * }); */ jasmine.setDefaultSpyStrategy = function(defaultStrategyFn) { return env.setDefaultSpyStrategy(defaultStrategyFn); }; return jasmineInterface; }; getJasmineRequireObj().Spy = function(j$) { var nextOrder = (function() { var order = 0; return function() { return order++; }; })(); var matchersUtil = new j$.MatchersUtil({ customTesters: [], pp: j$.makePrettyPrinter() }); /** * _Note:_ Do not construct this directly, use {@link spyOn}, {@link spyOnProperty}, {@link jasmine.createSpy}, or {@link jasmine.createSpyObj} * @constructor * @name Spy */ function Spy( name, originalFn, customStrategies, defaultStrategyFn, getPromise ) { var numArgs = typeof originalFn === 'function' ? originalFn.length : 0, wrapper = makeFunc(numArgs, function(context, args, invokeNew) { return spy(context, args, invokeNew); }), strategyDispatcher = new SpyStrategyDispatcher({ name: name, fn: originalFn, getSpy: function() { return wrapper; }, customStrategies: customStrategies, getPromise: getPromise }), callTracker = new j$.CallTracker(), spy = function(context, args, invokeNew) { /** * @name Spy.callData * @property {object} object - `this` context for the invocation. * @property {number} invocationOrder - Order of the invocation. * @property {Array} args - The arguments passed for this invocation. */ var callData = { object: context, invocationOrder: nextOrder(), args: Array.prototype.slice.apply(args) }; callTracker.track(callData); var returnValue = strategyDispatcher.exec(context, args, invokeNew); callData.returnValue = returnValue; return returnValue; }; function makeFunc(length, fn) { switch (length) { case 1: return function wrap1(a) { return fn(this, arguments, this instanceof wrap1); }; case 2: return function wrap2(a, b) { return fn(this, arguments, this instanceof wrap2); }; case 3: return function wrap3(a, b, c) { return fn(this, arguments, this instanceof wrap3); }; case 4: return function wrap4(a, b, c, d) { return fn(this, arguments, this instanceof wrap4); }; case 5: return function wrap5(a, b, c, d, e) { return fn(this, arguments, this instanceof wrap5); }; case 6: return function wrap6(a, b, c, d, e, f) { return fn(this, arguments, this instanceof wrap6); }; case 7: return function wrap7(a, b, c, d, e, f, g) { return fn(this, arguments, this instanceof wrap7); }; case 8: return function wrap8(a, b, c, d, e, f, g, h) { return fn(this, arguments, this instanceof wrap8); }; case 9: return function wrap9(a, b, c, d, e, f, g, h, i) { return fn(this, arguments, this instanceof wrap9); }; default: return function wrap() { return fn(this, arguments, this instanceof wrap); }; } } for (var prop in originalFn) { if (prop === 'and' || prop === 'calls') { throw new Error( "Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon" ); } wrapper[prop] = originalFn[prop]; } /** * @member {SpyStrategy} - Accesses the default strategy for the spy. This strategy will be used * whenever the spy is called with arguments that don't match any strategy * created with {@link Spy#withArgs}. * @name Spy#and * @since 2.0.0 * @example * spyOn(someObj, 'func').and.returnValue(42); */ wrapper.and = strategyDispatcher.and; /** * Specifies a strategy to be used for calls to the spy that have the * specified arguments. * @name Spy#withArgs * @since 3.0.0 * @function * @param {...*} args - The arguments to match * @type {SpyStrategy} * @example * spyOn(someObj, 'func').withArgs(1, 2, 3).and.returnValue(42); * someObj.func(1, 2, 3); // returns 42 */ wrapper.withArgs = function() { return strategyDispatcher.withArgs.apply(strategyDispatcher, arguments); }; wrapper.calls = callTracker; if (defaultStrategyFn) { defaultStrategyFn(wrapper.and); } return wrapper; } function SpyStrategyDispatcher(strategyArgs) { var baseStrategy = new j$.SpyStrategy(strategyArgs); var argsStrategies = new StrategyDict(function() { return new j$.SpyStrategy(strategyArgs); }); this.and = baseStrategy; this.exec = function(spy, args, invokeNew) { var strategy = argsStrategies.get(args); if (!strategy) { if (argsStrategies.any() && !baseStrategy.isConfigured()) { throw new Error( "Spy '" + strategyArgs.name + "' received a call with arguments " + j$.pp(Array.prototype.slice.call(args)) + ' but all configured strategies specify other arguments.' ); } else { strategy = baseStrategy; } } return strategy.exec(spy, args, invokeNew); }; this.withArgs = function() { return { and: argsStrategies.getOrCreate(arguments) }; }; } function StrategyDict(strategyFactory) { this.strategies = []; this.strategyFactory = strategyFactory; } StrategyDict.prototype.any = function() { return this.strategies.length > 0; }; StrategyDict.prototype.getOrCreate = function(args) { var strategy = this.get(args); if (!strategy) { strategy = this.strategyFactory(); this.strategies.push({ args: args, strategy: strategy }); } return strategy; }; StrategyDict.prototype.get = function(args) { var i; for (i = 0; i < this.strategies.length; i++) { if (matchersUtil.equals(args, this.strategies[i].args)) { return this.strategies[i].strategy; } } }; return Spy; }; getJasmineRequireObj().SpyFactory = function(j$) { function SpyFactory(getCustomStrategies, getDefaultStrategyFn, getPromise) { var self = this; this.createSpy = function(name, originalFn) { return j$.Spy( name, originalFn, getCustomStrategies(), getDefaultStrategyFn(), getPromise ); }; this.createSpyObj = function(baseName, methodNames, propertyNames) { var baseNameIsCollection = j$.isObject_(baseName) || j$.isArray_(baseName); if (baseNameIsCollection) { propertyNames = methodNames; methodNames = baseName; baseName = 'unknown'; } var obj = {}; var spy, descriptor; var methods = normalizeKeyValues(methodNames); for (var i = 0; i < methods.length; i++) { spy = obj[methods[i][0]] = self.createSpy( baseName + '.' + methods[i][0] ); if (methods[i].length > 1) { spy.and.returnValue(methods[i][1]); } } var properties = normalizeKeyValues(propertyNames); for (var i = 0; i < properties.length; i++) { descriptor = { get: self.createSpy(baseName + '.' + properties[i][0] + '.get'), set: self.createSpy(baseName + '.' + properties[i][0] + '.set') }; if (properties[i].length > 1) { descriptor.get.and.returnValue(properties[i][1]); descriptor.set.and.returnValue(properties[i][1]); } Object.defineProperty(obj, properties[i][0], descriptor); } if (methods.length === 0 && properties.length === 0) { throw 'createSpyObj requires a non-empty array or object of method names to create spies for'; } return obj; }; } function normalizeKeyValues(object) { var result = []; if (j$.isArray_(object)) { for (var i = 0; i < object.length; i++) { result.push([object[i]]); } } else if (j$.isObject_(object)) { for (var key in object) { if (object.hasOwnProperty(key)) { result.push([key, object[key]]); } } } return result; } return SpyFactory; }; getJasmineRequireObj().SpyRegistry = function(j$) { var spyOnMsg = j$.formatErrorMsg('', 'spyOn(, )'); var spyOnPropertyMsg = j$.formatErrorMsg( '', 'spyOnProperty(, , [accessType])' ); function SpyRegistry(options) { options = options || {}; var global = options.global || j$.getGlobal(); var createSpy = options.createSpy; var currentSpies = options.currentSpies || function() { return []; }; this.allowRespy = function(allow) { this.respy = allow; }; this.spyOn = function(obj, methodName) { var getErrorMsg = spyOnMsg; if (j$.util.isUndefined(obj) || obj === null) { throw new Error( getErrorMsg( 'could not find an object to spy upon for ' + methodName + '()' ) ); } if (j$.util.isUndefined(methodName) || methodName === null) { throw new Error(getErrorMsg('No method name supplied')); } if (j$.util.isUndefined(obj[methodName])) { throw new Error(getErrorMsg(methodName + '() method does not exist')); } if (obj[methodName] && j$.isSpy(obj[methodName])) { if (this.respy) { return obj[methodName]; } else { throw new Error( getErrorMsg(methodName + ' has already been spied upon') ); } } var descriptor = Object.getOwnPropertyDescriptor(obj, methodName); if (descriptor && !(descriptor.writable || descriptor.set)) { throw new Error( getErrorMsg(methodName + ' is not declared writable or has no setter') ); } var originalMethod = obj[methodName], spiedMethod = createSpy(methodName, originalMethod), restoreStrategy; if ( Object.prototype.hasOwnProperty.call(obj, methodName) || (obj === global && methodName === 'onerror') ) { restoreStrategy = function() { obj[methodName] = originalMethod; }; } else { restoreStrategy = function() { if (!delete obj[methodName]) { obj[methodName] = originalMethod; } }; } currentSpies().push({ restoreObjectToOriginalState: restoreStrategy }); obj[methodName] = spiedMethod; return spiedMethod; }; this.spyOnProperty = function(obj, propertyName, accessType) { var getErrorMsg = spyOnPropertyMsg; accessType = accessType || 'get'; if (j$.util.isUndefined(obj)) { throw new Error( getErrorMsg( 'spyOn could not find an object to spy upon for ' + propertyName + '' ) ); } if (j$.util.isUndefined(propertyName)) { throw new Error(getErrorMsg('No property name supplied')); } var descriptor = j$.util.getPropertyDescriptor(obj, propertyName); if (!descriptor) { throw new Error(getErrorMsg(propertyName + ' property does not exist')); } if (!descriptor.configurable) { throw new Error( getErrorMsg(propertyName + ' is not declared configurable') ); } if (!descriptor[accessType]) { throw new Error( getErrorMsg( 'Property ' + propertyName + ' does not have access type ' + accessType ) ); } if (j$.isSpy(descriptor[accessType])) { if (this.respy) { return descriptor[accessType]; } else { throw new Error( getErrorMsg( propertyName + '#' + accessType + ' has already been spied upon' ) ); } } var originalDescriptor = j$.util.clone(descriptor), spy = createSpy(propertyName, descriptor[accessType]), restoreStrategy; if (Object.prototype.hasOwnProperty.call(obj, propertyName)) { restoreStrategy = function() { Object.defineProperty(obj, propertyName, originalDescriptor); }; } else { restoreStrategy = function() { delete obj[propertyName]; }; } currentSpies().push({ restoreObjectToOriginalState: restoreStrategy }); descriptor[accessType] = spy; Object.defineProperty(obj, propertyName, descriptor); return spy; }; this.spyOnAllFunctions = function(obj) { if (j$.util.isUndefined(obj)) { throw new Error( 'spyOnAllFunctions could not find an object to spy upon' ); } var pointer = obj, props = [], prop, descriptor; while (pointer) { for (prop in pointer) { if ( Object.prototype.hasOwnProperty.call(pointer, prop) && pointer[prop] instanceof Function ) { descriptor = Object.getOwnPropertyDescriptor(pointer, prop); if ( (descriptor.writable || descriptor.set) && descriptor.configurable ) { props.push(prop); } } } pointer = Object.getPrototypeOf(pointer); } for (var i = 0; i < props.length; i++) { this.spyOn(obj, props[i]); } return obj; }; this.clearSpies = function() { var spies = currentSpies(); for (var i = spies.length - 1; i >= 0; i--) { var spyEntry = spies[i]; spyEntry.restoreObjectToOriginalState(); } }; } return SpyRegistry; }; getJasmineRequireObj().SpyStrategy = function(j$) { /** * @interface SpyStrategy */ function SpyStrategy(options) { options = options || {}; var self = this; /** * Get the identifying information for the spy. * @name SpyStrategy#identity * @since 3.0.0 * @member * @type {String} */ this.identity = options.name || 'unknown'; this.originalFn = options.fn || function() {}; this.getSpy = options.getSpy || function() {}; this.plan = this._defaultPlan = function() {}; var k, cs = options.customStrategies || {}; for (k in cs) { if (j$.util.has(cs, k) && !this[k]) { this[k] = createCustomPlan(cs[k]); } } var getPromise = typeof options.getPromise === 'function' ? options.getPromise : function() {}; var requirePromise = function(name) { var Promise = getPromise(); if (!Promise) { throw new Error( name + ' requires global Promise, or `Promise` configured with `jasmine.getEnv().configure()`' ); } return Promise; }; /** * Tell the spy to return a promise resolving to the specified value when invoked. * @name SpyStrategy#resolveTo * @since 3.5.0 * @function * @param {*} value The value to return. */ this.resolveTo = function(value) { var Promise = requirePromise('resolveTo'); self.plan = function() { return Promise.resolve(value); }; return self.getSpy(); }; /** * Tell the spy to return a promise rejecting with the specified value when invoked. * @name SpyStrategy#rejectWith * @since 3.5.0 * @function * @param {*} value The value to return. */ this.rejectWith = function(value) { var Promise = requirePromise('rejectWith'); self.plan = function() { return Promise.reject(value); }; return self.getSpy(); }; } function createCustomPlan(factory) { return function() { var plan = factory.apply(null, arguments); if (!j$.isFunction_(plan)) { throw new Error('Spy strategy must return a function'); } this.plan = plan; return this.getSpy(); }; } /** * Execute the current spy strategy. * @name SpyStrategy#exec * @since 2.0.0 * @function */ SpyStrategy.prototype.exec = function(context, args, invokeNew) { var contextArgs = [context].concat( args ? Array.prototype.slice.call(args) : [] ); var target = this.plan.bind.apply(this.plan, contextArgs); return invokeNew ? new target() : target(); }; /** * Tell the spy to call through to the real implementation when invoked. * @name SpyStrategy#callThrough * @since 2.0.0 * @function */ SpyStrategy.prototype.callThrough = function() { this.plan = this.originalFn; return this.getSpy(); }; /** * Tell the spy to return the value when invoked. * @name SpyStrategy#returnValue * @since 2.0.0 * @function * @param {*} value The value to return. */ SpyStrategy.prototype.returnValue = function(value) { this.plan = function() { return value; }; return this.getSpy(); }; /** * Tell the spy to return one of the specified values (sequentially) each time the spy is invoked. * @name SpyStrategy#returnValues * @since 2.1.0 * @function * @param {...*} values - Values to be returned on subsequent calls to the spy. */ SpyStrategy.prototype.returnValues = function() { var values = Array.prototype.slice.call(arguments); this.plan = function() { return values.shift(); }; return this.getSpy(); }; /** * Tell the spy to throw an error when invoked. * @name SpyStrategy#throwError * @since 2.0.0 * @function * @param {Error|Object|String} something Thing to throw */ SpyStrategy.prototype.throwError = function(something) { var error = j$.isString_(something) ? new Error(something) : something; this.plan = function() { throw error; }; return this.getSpy(); }; /** * Tell the spy to call a fake implementation when invoked. * @name SpyStrategy#callFake * @since 2.0.0 * @function * @param {Function} fn The function to invoke with the passed parameters. */ SpyStrategy.prototype.callFake = function(fn) { if (!(j$.isFunction_(fn) || j$.isAsyncFunction_(fn))) { throw new Error( 'Argument passed to callFake should be a function, got ' + fn ); } this.plan = fn; return this.getSpy(); }; /** * Tell the spy to do nothing when invoked. This is the default. * @name SpyStrategy#stub * @since 2.0.0 * @function */ SpyStrategy.prototype.stub = function(fn) { this.plan = function() {}; return this.getSpy(); }; SpyStrategy.prototype.isConfigured = function() { return this.plan !== this._defaultPlan; }; return SpyStrategy; }; getJasmineRequireObj().StackTrace = function(j$) { function StackTrace(error) { var lines = error.stack.split('\n').filter(function(line) { return line !== ''; }); var extractResult = extractMessage(error.message, lines); if (extractResult) { this.message = extractResult.message; lines = extractResult.remainder; } var parseResult = tryParseFrames(lines); this.frames = parseResult.frames; this.style = parseResult.style; } var framePatterns = [ // PhantomJS on Linux, Node, Chrome, IE, Edge // e.g. " at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)" // Note that the "function name" can include a surprisingly large set of // characters, including angle brackets and square brackets. { re: /^\s*at ([^\)]+) \(([^\)]+)\)$/, fnIx: 1, fileLineColIx: 2, style: 'v8' }, // NodeJS alternate form, often mixed in with the Chrome style // e.g. " at /some/path:4320:20 { re: /\s*at (.+)$/, fileLineColIx: 1, style: 'v8' }, // PhantomJS on OS X, Safari, Firefox // e.g. "run@http://localhost:8888/__jasmine__/jasmine.js:4320:27" // or "http://localhost:8888/__jasmine__/jasmine.js:4320:27" { re: /^(([^@\s]+)@)?([^\s]+)$/, fnIx: 2, fileLineColIx: 3, style: 'webkit' } ]; // regexes should capture the function name (if any) as group 1 // and the file, line, and column as group 2. function tryParseFrames(lines) { var style = null; var frames = lines.map(function(line) { var convertedLine = first(framePatterns, function(pattern) { var overallMatch = line.match(pattern.re), fileLineColMatch; if (!overallMatch) { return null; } fileLineColMatch = overallMatch[pattern.fileLineColIx].match( /^(.*):(\d+):\d+$/ ); if (!fileLineColMatch) { return null; } style = style || pattern.style; return { raw: line, file: fileLineColMatch[1], line: parseInt(fileLineColMatch[2], 10), func: overallMatch[pattern.fnIx] }; }); return convertedLine || { raw: line }; }); return { style: style, frames: frames }; } function first(items, fn) { var i, result; for (i = 0; i < items.length; i++) { result = fn(items[i]); if (result) { return result; } } } function extractMessage(message, stackLines) { var len = messagePrefixLength(message, stackLines); if (len > 0) { return { message: stackLines.slice(0, len).join('\n'), remainder: stackLines.slice(len) }; } } function messagePrefixLength(message, stackLines) { if (!stackLines[0].match(/^\w*Error/)) { return 0; } var messageLines = message.split('\n'); var i; for (i = 1; i < messageLines.length; i++) { if (messageLines[i] !== stackLines[i]) { return 0; } } return messageLines.length; } return StackTrace; }; getJasmineRequireObj().Suite = function(j$) { function Suite(attrs) { this.env = attrs.env; this.id = attrs.id; this.parentSuite = attrs.parentSuite; this.description = attrs.description; this.expectationFactory = attrs.expectationFactory; this.asyncExpectationFactory = attrs.asyncExpectationFactory; this.expectationResultFactory = attrs.expectationResultFactory; this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; this.beforeFns = []; this.afterFns = []; this.beforeAllFns = []; this.afterAllFns = []; this.timer = attrs.timer || new j$.Timer(); this.children = []; /** * @typedef SuiteResult * @property {Int} id - The unique id of this suite. * @property {String} description - The description text passed to the {@link describe} that made this suite. * @property {String} fullName - The full description including all ancestors of this suite. * @property {Expectation[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite. * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite. * @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite. * @property {number} duration - The time in ms for Suite execution, including any before/afterAll, before/afterEach. * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSuiteProperty} */ this.result = { id: this.id, description: this.description, fullName: this.getFullName(), failedExpectations: [], deprecationWarnings: [], duration: null, properties: null }; } Suite.prototype.setSuiteProperty = function(key, value) { this.result.properties = this.result.properties || {}; this.result.properties[key] = value; }; Suite.prototype.expect = function(actual) { return this.expectationFactory(actual, this); }; Suite.prototype.expectAsync = function(actual) { return this.asyncExpectationFactory(actual, this); }; Suite.prototype.getFullName = function() { var fullName = []; for ( var parentSuite = this; parentSuite; parentSuite = parentSuite.parentSuite ) { if (parentSuite.parentSuite) { fullName.unshift(parentSuite.description); } } return fullName.join(' '); }; Suite.prototype.pend = function() { this.markedPending = true; }; Suite.prototype.beforeEach = function(fn) { this.beforeFns.unshift(fn); }; Suite.prototype.beforeAll = function(fn) { this.beforeAllFns.push(fn); }; Suite.prototype.afterEach = function(fn) { this.afterFns.unshift(fn); }; Suite.prototype.afterAll = function(fn) { this.afterAllFns.unshift(fn); }; Suite.prototype.startTimer = function() { this.timer.start(); }; Suite.prototype.endTimer = function() { this.result.duration = this.timer.elapsed(); }; function removeFns(queueableFns) { for (var i = 0; i < queueableFns.length; i++) { queueableFns[i].fn = null; } } Suite.prototype.cleanupBeforeAfter = function() { removeFns(this.beforeAllFns); removeFns(this.afterAllFns); removeFns(this.beforeFns); removeFns(this.afterFns); }; Suite.prototype.addChild = function(child) { this.children.push(child); }; Suite.prototype.status = function() { if (this.markedPending) { return 'pending'; } if (this.result.failedExpectations.length > 0) { return 'failed'; } else { return 'passed'; } }; Suite.prototype.canBeReentered = function() { return this.beforeAllFns.length === 0 && this.afterAllFns.length === 0; }; Suite.prototype.getResult = function() { this.result.status = this.status(); return this.result; }; Suite.prototype.sharedUserContext = function() { if (!this.sharedContext) { this.sharedContext = this.parentSuite ? this.parentSuite.clonedSharedUserContext() : new j$.UserContext(); } return this.sharedContext; }; Suite.prototype.clonedSharedUserContext = function() { return j$.UserContext.fromExisting(this.sharedUserContext()); }; Suite.prototype.onException = function() { if (arguments[0] instanceof j$.errors.ExpectationFailed) { return; } var data = { matcherName: '', passed: false, expected: '', actual: '', error: arguments[0] }; var failedExpectation = this.expectationResultFactory(data); if (!this.parentSuite) { failedExpectation.globalErrorType = 'afterAll'; } this.result.failedExpectations.push(failedExpectation); }; Suite.prototype.addExpectationResult = function() { if (isFailure(arguments)) { var data = arguments[1]; this.result.failedExpectations.push(this.expectationResultFactory(data)); if (this.throwOnExpectationFailure) { throw new j$.errors.ExpectationFailed(); } } }; Suite.prototype.addDeprecationWarning = function(deprecation) { if (typeof deprecation === 'string') { deprecation = { message: deprecation }; } this.result.deprecationWarnings.push( this.expectationResultFactory(deprecation) ); }; function isFailure(args) { return !args[0]; } return Suite; }; if (typeof window == void 0 && typeof exports == 'object') { /* globals exports */ exports.Suite = jasmineRequire.Suite; } getJasmineRequireObj().Timer = function() { var defaultNow = (function(Date) { return function() { return new Date().getTime(); }; })(Date); function Timer(options) { options = options || {}; var now = options.now || defaultNow, startTime; this.start = function() { startTime = now(); }; this.elapsed = function() { return now() - startTime; }; } return Timer; }; getJasmineRequireObj().TreeProcessor = function() { function TreeProcessor(attrs) { var tree = attrs.tree, runnableIds = attrs.runnableIds, queueRunnerFactory = attrs.queueRunnerFactory, nodeStart = attrs.nodeStart || function() {}, nodeComplete = attrs.nodeComplete || function() {}, failSpecWithNoExpectations = !!attrs.failSpecWithNoExpectations, orderChildren = attrs.orderChildren || function(node) { return node.children; }, excludeNode = attrs.excludeNode || function(node) { return false; }, stats = { valid: true }, processed = false, defaultMin = Infinity, defaultMax = 1 - Infinity; this.processTree = function() { processNode(tree, true); processed = true; return stats; }; this.execute = function(done) { if (!processed) { this.processTree(); } if (!stats.valid) { throw 'invalid order'; } var childFns = wrapChildren(tree, 0); queueRunnerFactory({ queueableFns: childFns, userContext: tree.sharedUserContext(), onException: function() { tree.onException.apply(tree, arguments); }, onComplete: done }); }; function runnableIndex(id) { for (var i = 0; i < runnableIds.length; i++) { if (runnableIds[i] === id) { return i; } } } function processNode(node, parentExcluded) { var executableIndex = runnableIndex(node.id); if (executableIndex !== undefined) { parentExcluded = false; } if (!node.children) { var excluded = parentExcluded || excludeNode(node); stats[node.id] = { excluded: excluded, willExecute: !excluded && !node.markedPending, segments: [ { index: 0, owner: node, nodes: [node], min: startingMin(executableIndex), max: startingMax(executableIndex) } ] }; } else { var hasExecutableChild = false; var orderedChildren = orderChildren(node); for (var i = 0; i < orderedChildren.length; i++) { var child = orderedChildren[i]; processNode(child, parentExcluded); if (!stats.valid) { return; } var childStats = stats[child.id]; hasExecutableChild = hasExecutableChild || childStats.willExecute; } stats[node.id] = { excluded: parentExcluded, willExecute: hasExecutableChild }; segmentChildren(node, orderedChildren, stats[node.id], executableIndex); if (!node.canBeReentered() && stats[node.id].segments.length > 1) { stats = { valid: false }; } } } function startingMin(executableIndex) { return executableIndex === undefined ? defaultMin : executableIndex; } function startingMax(executableIndex) { return executableIndex === undefined ? defaultMax : executableIndex; } function segmentChildren( node, orderedChildren, nodeStats, executableIndex ) { var currentSegment = { index: 0, owner: node, nodes: [], min: startingMin(executableIndex), max: startingMax(executableIndex) }, result = [currentSegment], lastMax = defaultMax, orderedChildSegments = orderChildSegments(orderedChildren); function isSegmentBoundary(minIndex) { return ( lastMax !== defaultMax && minIndex !== defaultMin && lastMax < minIndex - 1 ); } for (var i = 0; i < orderedChildSegments.length; i++) { var childSegment = orderedChildSegments[i], maxIndex = childSegment.max, minIndex = childSegment.min; if (isSegmentBoundary(minIndex)) { currentSegment = { index: result.length, owner: node, nodes: [], min: defaultMin, max: defaultMax }; result.push(currentSegment); } currentSegment.nodes.push(childSegment); currentSegment.min = Math.min(currentSegment.min, minIndex); currentSegment.max = Math.max(currentSegment.max, maxIndex); lastMax = maxIndex; } nodeStats.segments = result; } function orderChildSegments(children) { var specifiedOrder = [], unspecifiedOrder = []; for (var i = 0; i < children.length; i++) { var child = children[i], segments = stats[child.id].segments; for (var j = 0; j < segments.length; j++) { var seg = segments[j]; if (seg.min === defaultMin) { unspecifiedOrder.push(seg); } else { specifiedOrder.push(seg); } } } specifiedOrder.sort(function(a, b) { return a.min - b.min; }); return specifiedOrder.concat(unspecifiedOrder); } function executeNode(node, segmentNumber) { if (node.children) { return { fn: function(done) { var onStart = { fn: function(next) { nodeStart(node, next); } }; queueRunnerFactory({ onComplete: function() { var args = Array.prototype.slice.call(arguments, [0]); node.cleanupBeforeAfter(); nodeComplete(node, node.getResult(), function() { done.apply(undefined, args); }); }, queueableFns: [onStart].concat(wrapChildren(node, segmentNumber)), userContext: node.sharedUserContext(), onException: function() { node.onException.apply(node, arguments); } }); } }; } else { return { fn: function(done) { node.execute( done, stats[node.id].excluded, failSpecWithNoExpectations ); } }; } } function wrapChildren(node, segmentNumber) { var result = [], segmentChildren = stats[node.id].segments[segmentNumber].nodes; for (var i = 0; i < segmentChildren.length; i++) { result.push( executeNode(segmentChildren[i].owner, segmentChildren[i].index) ); } if (!stats[node.id].willExecute) { return result; } return node.beforeAllFns.concat(result).concat(node.afterAllFns); } } return TreeProcessor; }; getJasmineRequireObj().UserContext = function(j$) { function UserContext() {} UserContext.fromExisting = function(oldContext) { var context = new UserContext(); for (var prop in oldContext) { if (oldContext.hasOwnProperty(prop)) { context[prop] = oldContext[prop]; } } return context; }; return UserContext; }; getJasmineRequireObj().version = function() { return '3.6.0'; }; gnome-shell-extension-gsconnect-50/installed-tests/jasmine.test.in000066400000000000000000000001241421543444100255570ustar00rootroot00000000000000[Test] Type=session Exec=@jasmine_path@ @installed_tests_execdir@/@name@ Output=TAP gnome-shell-extension-gsconnect-50/installed-tests/meson.build000066400000000000000000000034051421543444100247720ustar00rootroot00000000000000installed_tests_execdir = join_paths(libexecdir, 'installed-tests', meson.project_name()) installed_tests_metadir = join_paths(datadir, 'installed-tests', meson.project_name()) installed_tests_srcdir = meson.current_source_dir() installed_tests_builddir = meson.current_build_dir() installed_tests_gjspath = join_paths(installed_tests_builddir, 'src') # meson Test Environment test_env = environment() test_env.set('GSCONNECT_TEST', '1') test_env.set('GJS_PATH', installed_tests_gjspath) test_env.set('NO_AT_BRIDGE', '1') test_env.set('GSETTINGS_BACKEND', 'memory') test_env.set('GSETTINGS_SCHEMA_DIR', installed_tests_builddir) test_env.set('G_DEBUG', 'fatal-warnings,fatal-criticals') test_config_js = configure_file( input: '../data/config.js.in', output: 'config.js', configuration: extconfig, ) installed_tests_prepared = custom_target( 'prepare-tests', build_by_default: true, command: [ env_util, 'MESON_BUILD_ROOT=' + meson.build_root(), 'MESON_SOURCE_ROOT=' + meson.source_root(), 'G_TEST_BUILDDIR=' + installed_tests_builddir, 'G_TEST_SRCDIR=' + installed_tests_srcdir, join_paths(installed_tests_srcdir, 'prepare-tests.sh') ], output: 'none', ) # Bundled Jasmine runner minijasmine = find_program('minijasmine') minijasmine_path = join_paths(installed_tests_execdir, 'minijasmine') if get_option('installed_tests') install_data('jasmine.js', 'minijasmine', install_dir: installed_tests_execdir ) install_subdir('data', install_dir: installed_tests_execdir, ) install_subdir('fixtures', install_dir: installed_tests_execdir, ) install_data(test_config_js, install_dir: installed_tests_execdir, ) endif subdir('suites') gnome-shell-extension-gsconnect-50/installed-tests/minijasmine000077500000000000000000000111471421543444100250630ustar00rootroot00000000000000#!/usr/bin/env gjs const {GLib} = imports.gi; // Utils function _formatMessage(message) { return message.replace(/\n/g, '\\n'); } function _formatStack(stack) { if (!stack) return '# No stack'; return stack.split('\n') .filter(line => line.indexOf('') === -1) .filter(line => line.indexOf('minijasmine') === -1) .map(line => `# ${line}`) .join('\n'); } function _setTimeout(continueTimeout, func, time) { return GLib.timeout_add(GLib.PRIORITY_DEFAULT, time, function () { func(); return continueTimeout; }); } function _clearTimeout(id) { if (id > 0) GLib.source_remove(id); } /** * Reporter that outputs according to the Test Anything Protocol * See http://testanything.org/tap-specification.html */ class TapReporter { constructor() { this._failedSuites = []; this._specCount = 0; } jasmineStarted(info) { print(`1..${info.totalSpecsDefined}`); } jasmineDone() { this._failedSuites.forEach(failure => { failure.failedExpectations.forEach(result => { print('not ok - An error was thrown outside a test'); print(`# ${result.message}`); }); }); globalThis._jasmineMain.quit(); } suiteDone(result) { if (result.failedExpectations && result.failedExpectations.length > 0) { globalThis._jasmineRetval = 1; this._failedSuites.push(result); } if (result.status === 'disabled') print('# Suite was disabled:', result.fullName); } specStarted() { this._specCount++; } specDone(result) { let tapReport = 'ok'; if (result.status === 'failed') { globalThis._jasmineRetval = 1; tapReport = 'not ok'; } tapReport += ` ${this._specCount} ${result.fullName}`; if (result.status === 'pending' || result.status === 'disabled') tapReport += ` # SKIP ${result.pendingReason || result.status}`; print(tapReport); // Print additional diagnostic info on failure if (result.status === 'failed' && result.failedExpectations) { result.failedExpectations.forEach(failedExpectation => { print('# Message:', _formatMessage(failedExpectation.message)); print(`# Stack:\n${_formatStack(failedExpectation.stack)}`); }); } } } /** * Initialize Jasmine */ function initJasmine() { // Add the current path to the importer const thisFile = /@(.+):\d+/.exec(Error().stack.split('\n')[1])[1]; const thisDir = GLib.path_get_dirname(thisFile); imports.searchPath.unshift(thisDir); // Install the browser setTimeout/setInterval API on the global object globalThis.setTimeout = _setTimeout.bind(undefined, GLib.SOURCE_REMOVE); globalThis.setInterval = _setTimeout.bind(undefined, GLib.SOURCE_CONTINUE); globalThis.clearTimeout = globalThis.clearInterval = _clearTimeout; // Load Jasmine const jasmineRequire = imports.jasmine.getJasmineRequireObj(); const jasmineCore = jasmineRequire.core(jasmineRequire); globalThis._jasmineEnv = jasmineCore.getEnv(); globalThis._jasmineMain = GLib.MainLoop.new(null, false); globalThis._jasmineRetval = 0; // Install Jasmine API on the global object const iface = jasmineRequire.interface(jasmineCore, globalThis._jasmineEnv); Object.assign(globalThis, iface); // Register the TAP reporter globalThis._jasmineEnv.addReporter(new TapReporter()); } /** * Initialize test suites */ function initTests() { // Add the test path to the importer const testDir = GLib.path_get_dirname(ARGV[0]); imports.searchPath.unshift(testDir); // Load the test suites const testPath = GLib.path_get_basename(ARGV[0]).slice(0, -3); void imports[testPath]; } /** * Run initialized tests */ function runTests() { GLib.idle_add(GLib.PRIORITY_DEFAULT, function () { try { globalThis._jasmineEnv.execute(); } catch (e) { print('Bail out! Exception occurred inside Jasmine:', e); globalThis._jasmineRetval = 1; globalThis._jasmineMain.quit(); } return GLib.SOURCE_REMOVE; }); globalThis._jasmineMain.run(); // Exhaust the event loop const context = GLib.MainContext.default(); while (context.iteration(false)) continue; // Return the exit status imports.system.exit(globalThis._jasmineRetval); } // TODO: add CLI options for isolated XDG, DBus, etc initJasmine(); initTests(); runTests(); gnome-shell-extension-gsconnect-50/installed-tests/prepare-tests.sh000077500000000000000000000013021421543444100257570ustar00rootroot00000000000000#!/bin/bash APP_ID="org.gnome.Shell.Extensions.GSConnect" #G_TEST_BUILDDIR=${MESON_BUILD_ROOT}/installed-tests # Copy source files cp -R ${MESON_SOURCE_ROOT}/src ${G_TEST_BUILDDIR} cp ${G_TEST_BUILDDIR}/config.js ${G_TEST_BUILDDIR}/src # Compile GResources glib-compile-resources --external-data \ --sourcedir=${MESON_BUILD_ROOT}/data \ --sourcedir=${MESON_SOURCE_ROOT}/data \ --target=${G_TEST_BUILDDIR}/src/${APP_ID}.gresource \ ${MESON_SOURCE_ROOT}/data/${APP_ID}.gresource.xml # Compile GSettings Schema glib-compile-schemas --targetdir=${G_TEST_BUILDDIR} \ ${MESON_SOURCE_ROOT}/data gnome-shell-extension-gsconnect-50/installed-tests/suites/000077500000000000000000000000001421543444100241425ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/installed-tests/suites/backends/000077500000000000000000000000001421543444100257145ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/installed-tests/suites/backends/meson.build000066400000000000000000000020271421543444100300570ustar00rootroot00000000000000backend_tests_execdir = join_paths(installed_tests_execdir, 'backends') backend_tests_metadir = join_paths(installed_tests_metadir, 'backends') # Backend Suites backend_tests = [ 'LanBackend', ] foreach test : backend_tests test_file = files('test@0@.js'.format(test)) test(test, minijasmine, args: [test_file], depends: [installed_tests_prepared], env: test_env, protocol: 'tap', suite: 'backends' ) if get_option('installed_tests') test_config = configuration_data() test_config.set('jasmine_path', minijasmine_path) test_config.set('name', 'test@0@.js'.format(test)) test_config.set('installed_tests_execdir', backend_tests_execdir) test_description = configure_file( configuration: test_config, input: join_paths(installed_tests_srcdir, 'jasmine.test.in'), output: 'test@0@.test'.format(test), install_dir: backend_tests_metadir, ) install_data(test_file, install_dir: backend_tests_execdir, ) endif endforeach gnome-shell-extension-gsconnect-50/installed-tests/suites/backends/testLanBackend.js000066400000000000000000000106251421543444100311400ustar00rootroot00000000000000'use strict'; const {Gio, GLib} = imports.gi; const Utils = imports.fixtures.utils; const Core = imports.service.core; const Device = imports.service.device; const Lan = imports.service.backends.lan; describe('A LAN channel service', function () { let local, remote; let localChannel, remoteChannel; beforeAll(function () { const localCert = Gio.TlsCertificate.new_from_files( Utils.getDataPath('local-certificate.pem'), Utils.getDataPath('local-private.pem') ); local = new Lan.ChannelService({ id: GLib.uuid_string_random(), certificate: localCert, port: 1717, }); const remoteCert = Gio.TlsCertificate.new_from_files( Utils.getDataPath('remote-certificate.pem'), Utils.getDataPath('remote-private.pem') ); remote = new Lan.ChannelService({ id: GLib.uuid_string_random(), certificate: remoteCert, port: 1718, }); }); afterAll(function () { local.destroy(); remote.destroy(); }); it('can be started', function () { local.start(); expect(local.active).toBeTrue(); remote.start(); expect(remote.active).toBeTrue(); }); it('can request and accept channels', function (done) { const localId = local.connect('channel', (service, channel) => { local.disconnect(localId); localChannel = channel; if (localChannel && remoteChannel) done(); return true; }); const remoteId = remote.connect('channel', (service, channel) => { remote.disconnect(remoteId); remoteChannel = channel; if (localChannel && remoteChannel) done(); return true; }); local.broadcast('127.0.0.1:1718'); }); it('tracks active channels', function () { // NOTE: the broadcasting side uses it's own port for reconnect localChannel = local.channels.get(`lan://127.0.0.1:${local.port}`); expect(localChannel).toBeDefined(); remoteChannel = remote.channels.get(`lan://127.0.0.1:${local.port}`); expect(remoteChannel).toBeDefined(); }); describe('produces channels', function () { it('that can transfer packets', async function () { const outgoingPacket = new Core.Packet({ type: 'kdeconnect.test', body: { foo: GLib.uuid_string_random(), }, }); await localChannel.sendPacket(outgoingPacket); const incomingPacket = await remoteChannel.readPacket(); expect(incomingPacket.type).toBe(outgoingPacket.type); expect(incomingPacket.body.foo).toBe(outgoingPacket.body.foo); }); it('that can transfer payloads', async function () { // Uploading Channel const outgoingPacket = new Core.Packet({ type: 'kdeconnect.test', body: {foo: 'bar'}, }); const sentBytes = new GLib.Bytes(GLib.uuid_string_random()); const inputStream = Gio.MemoryInputStream.new_from_bytes(sentBytes); const localTransfer = new Core.Transfer({channel: localChannel}); localTransfer.addStream(outgoingPacket, inputStream, sentBytes.get_size()); localTransfer.start().catch(e => logError(e)); // Downloading Channel const incomingPacket = await remoteChannel.readPacket(); const outputStream = Gio.MemoryOutputStream.new_resizable(); const remoteTransfer = new Core.Transfer({channel: remoteChannel}); remoteTransfer.addStream(incomingPacket, outputStream); await remoteTransfer.start(); const receivedBytes = outputStream.steal_as_bytes(); expect(receivedBytes.equal(sentBytes)).toBeTrue(); }); }); it('can be stopped', function () { local.stop(); expect(local.active).toBeFalse(); remote.stop(); expect(remote.active).toBeFalse(); }); it('closes active channels when stopped', function () { expect(local.channels).toHaveSize(0); localChannel = null; expect(remote.channels).toHaveSize(0); remoteChannel = null; }); // TODO: restarting stopped services }); gnome-shell-extension-gsconnect-50/installed-tests/suites/components/000077500000000000000000000000001421543444100263275ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/installed-tests/suites/components/meson.build000066400000000000000000000023651421543444100304770ustar00rootroot00000000000000component_tests_execdir = join_paths(installed_tests_execdir, 'components') component_tests_metadir = join_paths(installed_tests_metadir, 'components') # Component Suites component_tests = [ 'ClipboardComponent', # 'ContactsComponent', # 'InputComponent', 'MprisComponent', # 'NotificationComponent', # 'PulseaudioComponent', # 'SessionComponent', # 'SoundComponent', # 'UpowerComponent', ] foreach test : component_tests test_file = files('test@0@.js'.format(test)) test(test, minijasmine, args: [test_file], depends: [installed_tests_prepared], env: test_env, protocol: 'tap', suite: 'components' ) if get_option('installed_tests') test_config = configuration_data() test_config.set('jasmine_path', minijasmine_path) test_config.set('name', 'test@0@.js'.format(test)) test_config.set('installed_tests_execdir', component_tests_execdir) test_description = configure_file( configuration: test_config, input: join_paths(installed_tests_srcdir, 'jasmine.test.in'), output: 'test@0@.test'.format(test), install_dir: component_tests_metadir, ) install_data(test_file, install_dir: component_tests_execdir, ) endif endforeach gnome-shell-extension-gsconnect-50/installed-tests/suites/components/testClipboardComponent.js000066400000000000000000000023461421543444100333540ustar00rootroot00000000000000'use strict'; const Utils = imports.fixtures.utils; const {Gdk, Gtk, Gio, GLib} = imports.gi; const {Clipboard} = imports.service.components.clipboard; describe('The Clipboard component', function () { let clipboard; let gtkClipboard; beforeAll(function () { Gtk.init(null); const display = Gdk.Display.get_default(); gtkClipboard = Gtk.Clipboard.get_default(display); clipboard = new Clipboard(); }); afterAll(function () { clipboard.destroy(); }); it('pulls changes from the session clipboard', function (done) { const text = GLib.uuid_string_random(); const id = clipboard.connect('notify::text', (clipboard) => { clipboard.disconnect(id); expect(clipboard.text).toBe(text); done(); }); gtkClipboard.set_text(text, -1); }); it('pushes changes to the session clipboard', function (done) { const text = GLib.uuid_string_random(); const id = gtkClipboard.connect('owner-change', (gtkClipboard) => { gtkClipboard.disconnect(id); expect(gtkClipboard.wait_for_text()).toBe(text); done(); }); clipboard.text = text; }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/components/testMprisComponent.js000066400000000000000000000072271421543444100325520ustar00rootroot00000000000000'use strict'; const {Gio, GLib} = imports.gi; const Utils = imports.fixtures.utils; const {MockPlayer} = imports.fixtures.mpris; const MPRIS = imports.service.components.mpris; // Prevent auto-loading MPRIS.Manager.prototype._loadPlayers = function () {}; describe('The MPRIS component', function () { let manager; let player; beforeAll(function () { manager = new MPRIS.Manager(); player = new MockPlayer(GLib.uuid_string_random()); }); afterAll(function () { manager.destroy(); }); describe('emits a signal', function () { it('when players appear on the bus', function (done) { const id = manager.connect('player-added', (manager, proxy) => { manager.disconnect(id); expect(proxy.Identity).toBe(player.Identity); done(); }); player.export(); }); it('when players are changed', function (done) { const id = manager.connect('player-changed', (manager, proxy) => { manager.disconnect(id); expect(proxy.Volume).toBe(0.5); done(); }); player.Volume = 0.5; }); it('when players are seeked', function (done) { const id = manager.connect('player-seeked', (manager, proxy, offset) => { manager.disconnect(id); expect(offset).toBe(1000); done(); }); player.emit('Seeked', 1000); }); it('when players vanish from the bus', function (done) { const id = manager.connect('player-removed', (manager, proxy) => { manager.disconnect(id); expect(proxy.Identity).toBe(player.Identity); done(); }); player.unexport(); }); }); describe('can track players', function () { beforeAll(function (done) { const id = manager.connect('player-added', (manager, proxy) => { manager.disconnect(id); done(); }); // Prep for pause/unpause tests player._CanPause = true; player._CanPlay = true; player._PlaybackStatus = 'Playing'; player.export(); }); afterAll(function (done) { const id = manager.connect('player-removed', (manager, proxy) => { manager.disconnect(id); done(); }); player.unexport(); }); it('and check for them', function () { expect(manager.hasPlayer(player.Identity)).toBeTrue(); }); it('and retrieve them', function () { const proxy = manager.getPlayer(player.Identity); expect(proxy.Identity).toBe(player.Identity); }); it('and list their identities', function () { expect(manager.getIdentities()).toContain(player.Identity); }); it('and pause them as a group', function (done) { const id = player.connect('notify::PlaybackStatus', (player) => { player.disconnect(id); expect(player.PlaybackStatus).toBe('Paused'); done(); }); manager.pauseAll(); }); it('and unpause them as a group', function (done) { pending('fix property propagation'); const id = player.connect('notify::PlaybackStatus', (player) => { player.disconnect(id); expect(player.PlaybackStatus).toBe('Playing'); done(); }); manager.unpauseAll(); }); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/core/000077500000000000000000000000001421543444100250725ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/installed-tests/suites/core/meson.build000066400000000000000000000020241421543444100272320ustar00rootroot00000000000000core_tests_execdir = join_paths(installed_tests_execdir, 'core') core_tests_metadir = join_paths(installed_tests_metadir, 'core') # Core Suites core_tests = [ 'Device', 'Manager', 'Packet', 'Plugin', ] foreach test : core_tests test_file = files('test@0@.js'.format(test)) test(test, minijasmine, args: [test_file], depends: [installed_tests_prepared], env: test_env, protocol: 'tap', suite: 'core' ) if get_option('installed_tests') test_config = configuration_data() test_config.set('jasmine_path', minijasmine_path) test_config.set('name', 'test@0@.js'.format(test)) test_config.set('installed_tests_execdir', core_tests_execdir) test_description = configure_file( configuration: test_config, input: join_paths(installed_tests_srcdir, 'jasmine.test.in'), output: 'test@0@.test'.format(test), install_dir: core_tests_metadir, ) install_data(test_file, install_dir: core_tests_execdir, ) endif endforeach gnome-shell-extension-gsconnect-50/installed-tests/suites/core/testDevice.js000066400000000000000000000060251421543444100275320ustar00rootroot00000000000000'use strict'; const {Gio, GLib} = imports.gi; const Utils = imports.fixtures.utils; const Device = imports.service.device; describe('A device constructed from a packet', function () { let device, identity; beforeAll(function () { identity = Utils.generateIdentity({ body: { incomingCapabilities: ['kdeconnect.ping'], outgoingCapabilities: ['kdeconnect.ping'], }, }); device = new Device.Device(identity); }); afterAll(function () { device.destroy(); }); it('initializes properties', function () { expect(device.id).toBe(identity.body.deviceId); expect(device.name).toBe(identity.body.deviceName); expect(device.type).toBe(identity.body.deviceType); // expect(device.contacts).toBeTruthy(); expect(device.encryption_info).toBeTruthy(); expect(device.icon_name).toBeTruthy(); expect(device.connected).toBeFalse(); expect(device.paired).toBeFalse(); expect(device.settings).toBeInstanceOf(Gio.Settings); expect(device.menu).toBeInstanceOf(Gio.Menu); }); it('will not load plugins when unpaired', async function () { await device._loadPlugins(); expect(device._plugins).toHaveSize(0); }); it('will load plugins when paired', async function () { device._setPaired(true); expect(device.paired).toBeTrue(); await device._loadPlugins(); expect(device._plugins).toHaveSize(1); }); it('unloads plugins when unpaired', function () { device.unpair(); expect(device.paired).toBeFalse(); expect(device._plugins).toHaveSize(0); }); }); describe('A device constructed from an ID', function () { let device, id; beforeAll(function () { id = GLib.uuid_string_random(); device = new Device.Device({body: {deviceId: id}}); }); afterAll(function () { device.destroy(); }); it('initializes properties', function () { expect(device.id).toBe(id); expect(device.name).toBe(''); expect(device.type).toBe('smartphone'); // expect(device.contacts).toBeTruthy(); expect(device.encryption_info).toBeTruthy(); expect(device.icon_name).toBeTruthy(); expect(device.connected).toBeFalse(); expect(device.paired).toBeFalse(); expect(device.settings).toBeInstanceOf(Gio.Settings); expect(device.menu).toBeInstanceOf(Gio.Menu); }); it('will not load plugins when unpaired', function () { device._loadPlugins(); expect(device._plugins).toHaveSize(0); }); it('will load plugins when paired', function () { device._setPaired(true); expect(device.paired).toBeTrue(); device._loadPlugins(); expect(device._plugins).toHaveSize(0); }); it('will unload plugins when unpaired', function () { device.unpair(); expect(device.paired).toBeFalse(); expect(device._plugins).toHaveSize(0); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/core/testManager.js000066400000000000000000000045431421543444100277100ustar00rootroot00000000000000'use strict'; const {GLib} = imports.gi; const Utils = imports.fixtures.utils; const {Manager} = imports.service.manager; // TODO: * device management // * DBus describe('Manager', function () { let manager; let testRig; beforeAll(function () { manager = new Manager({ object_path: '/org/gnome/Shell/Extensions/GSConnect/Test', }); testRig = new Utils.TestRig(); spyOn(manager, '_loadDevices').and.callThrough(); spyOn(manager, '_loadBackends'); }); afterAll(function () { manager.destroy(); testRig.destroy(); }); it('sets defaults for required properties', function () { expect(manager.id).toBeTruthy(); expect(manager.name).toBeTruthy(); }); it('can be started', function () { manager.start(); // Disable auto-reconnect GLib.Source.remove(manager._reconnectId); manager._reconnectId = 0; expect(manager.active).toBeTrue(); expect(manager._loadDevices).toHaveBeenCalled(); expect(manager._loadBackends).toHaveBeenCalled(); }); it('can create devices for channels', function (done) { const {localService, remoteService} = testRig; // Managed service manager.backends.set('mock', localService); localService.__channelId = localService.connect('channel', manager._onChannel.bind(manager)); localService.start(); // Unmanaged service const id1 = remoteService.connect('channel', (service, channel) => { service.disconnect(id1); testRig.remoteChannel = channel; return true; }); remoteService.start(); // const id2 = manager.settings.connect('changed::devices', (settings) => { settings.disconnect(id2); expect(manager.devices).toHaveSize(1); done(); }); manager.identify(`mock://127.0.0.1:${remoteService.port}`); }); it('can be stopped', function () { manager.stop(); expect(manager.active).toBeFalse(); expect(manager.devices).toHaveSize(0); expect(manager.backends).toHaveSize(0); }); it('loads cached devices when started', function () { manager.start(); expect(manager.devices).toHaveSize(1); manager.stop(); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/core/testPacket.js000066400000000000000000000061341421543444100275430ustar00rootroot00000000000000'use strict'; const {Gio, GLib} = imports.gi; const Utils = imports.fixtures.utils; const Core = imports.service.core; /* * Test Packets */ const ObjectPacket = { id: Date.now(), type: 'kdeconnect.foo', body: { bar: 'baz', }, }; const PayloadPacket = { id: Date.now(), type: 'kdeconnect.foo', body: { bar: 'baz', }, payloadSize: Math.random() * 100, payloadTransferInfo: {port: 1739}, }; const DataPacket = `{ "id": 1234, "type": "kdeconnect.foo", "body": { "bar": "baz" } }`; describe('A packet', function () { let dataData, dataPacket; let objectData, objectPacket; let payloadData, payloadPacket; it('can be deserialized from an object', function () { objectPacket = new Core.Packet(ObjectPacket); expect(objectPacket.id).toBe(ObjectPacket.id); expect(objectPacket.type).toBe(ObjectPacket.type); expect(objectPacket.body.bar).toBe(ObjectPacket.body.bar); payloadPacket = new Core.Packet(PayloadPacket); expect(payloadPacket.id).toBe(PayloadPacket.id); expect(payloadPacket.type).toBe(PayloadPacket.type); expect(payloadPacket.body.bar).toBe(PayloadPacket.body.bar); }); it('can be deserialized from a data stream', function () { dataPacket = new Core.Packet(DataPacket); expect(dataPacket.id).toBe(1234); expect(dataPacket.type).toBe('kdeconnect.foo'); expect(dataPacket.body.bar).toBe('baz'); }); it('can be serialized to a data stream', function () { dataData = dataPacket.serialize(); expect(dataData[dataData.length - 1]).toBe('\n'); objectData = objectPacket.serialize(); expect(objectData[objectData.length - 1]).toBe('\n'); payloadData = payloadPacket.serialize(); expect(payloadData[payloadData.length - 1]).toBe('\n'); }); it('that has been serialized can be deserialized', function () { dataPacket = Core.Packet.deserialize(dataData); expect(dataPacket.id).not.toBe(1234); expect(dataPacket.type).toBe('kdeconnect.foo'); expect(dataPacket.body.bar).toBe('baz'); objectPacket = Core.Packet.deserialize(objectData); expect(objectPacket.id).not.toBe(ObjectPacket.id); expect(objectPacket.type).toBe(ObjectPacket.type); expect(objectPacket.body.bar).toBe(ObjectPacket.body.bar); payloadPacket = Core.Packet.deserialize(payloadData); expect(payloadPacket.id).not.toBe(PayloadPacket.id); expect(payloadPacket.type).toBe(PayloadPacket.type); expect(payloadPacket.body.bar).toBe(PayloadPacket.body.bar); }); it('can be converted to a useful string representation', function () { expect(dataPacket.toString()).toBe('[object Packet:kdeconnect.foo]'); expect(objectPacket.toString()).toBe('[object Packet:kdeconnect.foo]'); }); it('can check for a payload', function () { expect(dataPacket.hasPayload()).toBeFalse(); expect(objectPacket.hasPayload()).toBeFalse(); expect(payloadPacket.hasPayload()).toBeTrue(); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/core/testPlugin.js000066400000000000000000000160511421543444100275710ustar00rootroot00000000000000'use strict'; const {Gio, GLib, GObject} = imports.gi; const Utils = imports.fixtures.utils; const Device = imports.service.device; const Components = imports.service.components; const PluginBase = imports.service.plugin; /* * Phony plugin */ var Metadata = { label: 'FooBarBaz', id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.FooBarBaz', incomingCapabilities: ['kdeconnect.foo'], outgoingCapabilities: ['kdeconnect.bar', 'kdeconnect.qux'], actions: { foo: { label: 'Foo', icon_name: 'dialog-information-symbolic', parameter_type: new GLib.VariantType('a{sv}'), incoming: ['kdeconnect.foo'], outgoing: [], }, bar: { label: 'Bar', icon_name: 'dialog-information-symbolic', parameter_type: new GLib.VariantType('b'), incoming: [], outgoing: ['kdeconnect.bar'], }, baz: { label: 'Baz', icon_name: 'dialog-information-symbolic', parameter_type: null, incoming: ['kdeconnect.foo'], outgoing: ['kdeconnect.baz'], }, }, }; const TestPlugin = GObject.registerClass({ GTypeName: 'GSConnectTestPlugin', }, class TestPlugin extends PluginBase.Plugin { _init(device) { super._init(device, 'foobarbaz', Metadata); this._cacheLoaded = false; this.data = 'default'; this._foo = null; } /* * Class methods */ cacheLoaded() { this._cacheLoaded = true; } cacheClear() { } connected() { super.connected(); } disconnected() { super.disconnected(); } handlePacket(packet) { } /* * Instance methods */ foo(params) { this._foo = params; } bar(bool) { this.device.sendPacket({ type: 'kdeconnect.bar', body: { bar: bool, }, }); } baz() { } destroy() { if (this._someComponent !== undefined) this._someComponent = Components.release('notification'); super.destroy(); } }); describe('Plugin GActions', function () { let device, plugin; beforeAll(function () { const identity = Utils.generateIdentity({ body: { incomingCapabilities: Metadata.outgoingCapabilities, outgoingCapabilities: Metadata.incomingCapabilities, }, }); device = new Device.Device(identity); plugin = new TestPlugin(device); spyOn(plugin, 'foo').and.callThrough(); }); afterAll(function () { device.destroy(); }); it('are registered when constructed', function () { expect(device.has_action('foo')).toBeTrue(); expect(device.has_action('bar')).toBeTrue(); expect(device.has_action('baz')).toBeTrue(); }); it('are registered with the correct parameter type', function () { const fooParam = device.lookup_action('foo').get_parameter_type(); expect(fooParam.equal(Metadata.actions.foo.parameter_type)).toBeTrue(); const barParam = device.lookup_action('bar').get_parameter_type(); expect(barParam.equal(Metadata.actions.bar.parameter_type)).toBeTrue(); const bazParam = device.lookup_action('baz').get_parameter_type(); expect(bazParam).toBe(Metadata.actions.baz.parameter_type); }); it('are enabled when connected', function () { plugin.connected(); expect(plugin._gactions.some(action => action.enabled)).toBeTrue(); }); it('are only enabled if supported by the device', function () { expect(device.get_action_enabled('foo')).toBeTrue(); expect(device.get_action_enabled('bar')).toBeTrue(); expect(device.get_action_enabled('baz')).toBeFalse(); }); it('can be activated with complex parameters', function () { const parameter = new GLib.Variant('a{sv}', { 'string': GLib.Variant.new_string('qux'), 'icon': Gio.Icon.new_for_string('dialog-error-symbolic').serialize(), 'nested': new GLib.Variant('a{sb}', {'key': true}), }); device.lookup_action('foo').activate(parameter); expect(plugin.foo).toHaveBeenCalled(); expect(plugin._foo.string).toBe('qux'); expect(plugin._foo.icon instanceof Gio.Icon).toBeTrue(); expect(plugin._foo.nested.key).toBeTrue(); }); it('are disabled when disconnected', function () { plugin.disconnected(); expect(plugin._gactions.some(action => action.enabled)).toBeFalse(); }); it('are unregistered when destroyed', function () { plugin.destroy(); expect(device.has_action('foo')).toBeFalse(); expect(device.has_action('bar')).toBeFalse(); expect(device.has_action('baz')).toBeFalse(); }); }); describe('Plugin packets', function () { let device, plugin; beforeAll(function () { const identity = Utils.generateIdentity({ body: { incomingCapabilities: Metadata.outgoingCapabilities, outgoingCapabilities: Metadata.incomingCapabilities, }, }); device = new Device.Device(identity); plugin = new TestPlugin(device); device._plugins.set('foobarbaz', plugin); device._handlers.set('kdeconnect.foo', plugin); device._setPaired(true); plugin.connected(); spyOn(device, 'sendPacket'); spyOn(plugin, 'handlePacket'); spyOn(plugin, 'bar').and.callThrough(); }); afterAll(function () { device._plugins.delete('foobarbaz'); device._handlers.delete('kdeconnect.foo'); plugin.destroy(); device.destroy(); }); it('can be sent by function', function () { plugin.bar(true); expect(device.sendPacket).toHaveBeenCalled(); }); it('can be sent by GAction', function () { device.lookup_action('bar').activate(new GLib.Variant('b', false)); expect(plugin.bar).toHaveBeenCalledWith(false); expect(device.sendPacket).toHaveBeenCalled(); }); it('can be received if supported', function () { const packet = { type: 'kdeconnect.foo', body: {}, }; device.handlePacket(packet); expect(plugin.handlePacket).toHaveBeenCalledWith(packet); }); }); // TODO describe('Plugin cache', function () { let device, plugin; beforeAll(function () { const identity = Utils.generateIdentity({ body: { incomingCapabilities: Metadata.outgoingCapabilities, outgoingCapabilities: Metadata.incomingCapabilities, }, }); device = new Device.Device(identity); plugin = new TestPlugin(device); }); afterAll(function () { plugin.destroy(); device.destroy(); }); it('can be loaded', async function () { await expectAsync(plugin.cacheProperties(['data'])).toBeResolved(); expect(plugin._cacheLoaded).toBeTrue(); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/meson.build000066400000000000000000000001121421543444100262760ustar00rootroot00000000000000subdir('core') subdir('backends') subdir('components') subdir('plugins') gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/000077500000000000000000000000001421543444100256235ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/meson.build000066400000000000000000000024651421543444100277740ustar00rootroot00000000000000plugin_tests_execdir = join_paths(installed_tests_execdir, 'plugins') plugin_tests_metadir = join_paths(installed_tests_metadir, 'plugins') # Plugin Suites plugin_tests = [ 'BatteryPlugin', 'ClipboardPlugin', 'ContactsPlugin', 'FindmyphonePlugin', 'MousepadPlugin', 'MprisPlugin', 'NotificationPlugin', 'PhotoPlugin', 'PingPlugin', 'PresenterPlugin', 'RuncommandPlugin', 'SftpPlugin', 'SharePlugin', 'SmsPlugin', 'SystemvolumePlugin', 'TelephonyPlugin', ] foreach test : plugin_tests test_file = files('test@0@.js'.format(test)) test(test, minijasmine, args: [test_file], depends: [installed_tests_prepared], env: test_env, protocol: 'tap', suite: 'plugins' ) if get_option('installed_tests') test_config = configuration_data() test_config.set('jasmine_path', minijasmine_path) test_config.set('name', 'test@0@.js'.format(test)) test_config.set('installed_tests_execdir', plugin_tests_execdir) test_description = configure_file( configuration: test_config, input: join_paths(installed_tests_srcdir, 'jasmine.test.in'), output: 'test@0@.test'.format(test), install_dir: plugin_tests_metadir, ) install_data(test_file, install_dir: plugin_tests_execdir, ) endif endforeach gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/testBatteryPlugin.js000066400000000000000000000153171421543444100316610ustar00rootroot00000000000000'use strict'; const Utils = imports.fixtures.utils; const Packets = { goodBattery: { type: 'kdeconnect.battery', body: { currentCharge: 50, isCharging: false, thresholdEvent: 0, }, }, lowBattery: { type: 'kdeconnect.battery', body: { currentCharge: 15, isCharging: false, thresholdEvent: 1, }, }, customBattery: { type: 'kdeconnect.battery', body: { currentCharge: 80, isCharging: true, thresholdEvent: 0, }, }, fullBattery: { type: 'kdeconnect.battery', body: { currentCharge: 100, isCharging: true, thresholdEvent: 0, }, }, }; describe('The battery plugin', function () { let testRig; let localPlugin, remotePlugin; beforeAll(async function () { Utils.mockComponents(); testRig = new Utils.TestRig(); await testRig.prepare({ localDevice: { incomingCapabilities: [ 'kdeconnect.battery', 'kdeconnect.battery.request', ], outgoingCapabilities: [ 'kdeconnect.battery', 'kdeconnect.battery.request', ], }, remoteDevice: { incomingCapabilities: [ 'kdeconnect.battery', 'kdeconnect.battery.request', ], outgoingCapabilities: [ 'kdeconnect.battery', 'kdeconnect.battery.request', ], }, }); testRig.setPaired(true); }); afterAll(function () { testRig.destroy(); }); beforeEach(function () { if (localPlugin && remotePlugin) { spyOn(remotePlugin, 'handlePacket').and.callThrough(); spyOn(remotePlugin, '_receiveState').and.callThrough(); spyOn(remotePlugin, '_requestState').and.callThrough(); spyOn(remotePlugin, '_sendState').and.callThrough(); spyOn(remotePlugin.device, 'showNotification'); spyOn(remotePlugin.device, 'hideNotification'); } }); it('can be loaded', async function () { await testRig.loadPlugins(); localPlugin = testRig.localDevice._plugins.get('battery'); remotePlugin = testRig.remoteDevice._plugins.get('battery'); expect(localPlugin).toBeDefined(); expect(remotePlugin).toBeDefined(); }); it('sends and requests state updates when connected', async function () { testRig.setConnected(true); await remotePlugin.awaitPacket('kdeconnect.battery.request'); expect(remotePlugin._requestState).toHaveBeenCalled(); expect(remotePlugin._sendState).toHaveBeenCalled(); }); it('can receive state updates', async function () { localPlugin.device.sendPacket(Packets.goodBattery); await remotePlugin.awaitPacket('kdeconnect.battery', Packets.goodBattery.body); expect(remotePlugin._receiveState).toHaveBeenCalled(); }); it('updates properties', function () { expect(remotePlugin.charging).toBeFalse(); expect(remotePlugin.icon_name).toBe('battery-good-symbolic'); expect(remotePlugin.level).toBe(50); expect(remotePlugin.time).toBeGreaterThan(0); }); it('updates the GAction state', function () { const batteryAction = remotePlugin.device.lookup_action('battery'); const [charging, icon, level, time] = batteryAction.state.deepUnpack(); expect(charging).toBeFalse(); expect(icon).toBe('battery-good-symbolic'); expect(level).toBe(50); expect(time).toBeGreaterThan(0); }); it('notifies when the battery is low', async function () { localPlugin.device.sendPacket(Packets.lowBattery); await remotePlugin.awaitPacket('kdeconnect.battery', Packets.lowBattery.body); expect(remotePlugin.device.showNotification).toHaveBeenCalled(); }); it('withdraws low battery notifications', async function () { localPlugin.device.sendPacket(Packets.goodBattery); await remotePlugin.awaitPacket('kdeconnect.battery', Packets.goodBattery.body); expect(remotePlugin.device.hideNotification).toHaveBeenCalled(); }); it('notifies when the battery is at custom level', async function () { remotePlugin.settings.set_boolean('custom-battery-notification', true); localPlugin.device.sendPacket(Packets.customBattery); await remotePlugin.awaitPacket('kdeconnect.battery', Packets.customBattery.body); expect(remotePlugin.device.showNotification).toHaveBeenCalled(); }); it('withdraws custom battery notifications', async function () { localPlugin.device.sendPacket(Packets.goodBattery); await remotePlugin.awaitPacket('kdeconnect.battery', Packets.goodBattery.body); expect(remotePlugin.device.hideNotification).toHaveBeenCalled(); }); it('notifies when the battery is full', async function () { remotePlugin.settings.set_boolean('full-battery-notification', true); localPlugin.device.sendPacket(Packets.fullBattery, Packets.fullBattery.body); await remotePlugin.awaitPacket('kdeconnect.battery'); expect(remotePlugin.device.showNotification).toHaveBeenCalled(); }); it('withdraws full battery notifications', async function () { localPlugin.device.sendPacket(Packets.goodBattery); await remotePlugin.awaitPacket('kdeconnect.battery', Packets.goodBattery.body); expect(remotePlugin.device.hideNotification).toHaveBeenCalled(); }); describe('sends local statistics', function () { it('when enabled', async function () { localPlugin.settings.set_boolean('send-statistics', true); await remotePlugin.awaitPacket('kdeconnect.battery'); }); it('when they change', async function () { localPlugin._upower.update({ charging: true, level: 50, threshold: 0, }); await remotePlugin.awaitPacket('kdeconnect.battery', { currentCharge: 50, isCharging: true, thresholdEvent: 0, }); }); it('only if available', function () { spyOn(localPlugin.device, 'sendPacket'); localPlugin._upower.update({ is_present: false, }); expect(localPlugin.device.sendPacket).not.toHaveBeenCalled(); }); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/testClipboardPlugin.js000066400000000000000000000100771421543444100321440ustar00rootroot00000000000000'use strict'; const Utils = imports.fixtures.utils; describe('The clipboard plugin', function () { let testRig; let localPlugin, remotePlugin; beforeAll(async function () { Utils.mockComponents(); testRig = new Utils.TestRig(); await testRig.prepare({ localDevice: { incomingCapabilities: [ 'kdeconnect.clipboard', 'kdeconnect.clipboard.connect', ], outgoingCapabilities: [ 'kdeconnect.clipboard', 'kdeconnect.clipboard.connect', ], }, remoteDevice: { incomingCapabilities: [ 'kdeconnect.clipboard', 'kdeconnect.clipboard.connect', ], outgoingCapabilities: [ 'kdeconnect.clipboard', 'kdeconnect.clipboard.connect', ], }, }); testRig.setPaired(true); }); afterAll(function () { testRig.destroy(); }); beforeEach(function () { if (remotePlugin && localPlugin) { spyOn(remotePlugin, 'handlePacket').and.callThrough(); spyOn(localPlugin.device, 'sendPacket').and.callThrough(); } }); it('can be loaded', async function () { await testRig.loadPlugins(); localPlugin = testRig.localDevice._plugins.get('clipboard'); remotePlugin = testRig.remoteDevice._plugins.get('clipboard'); expect(localPlugin).toBeDefined(); expect(remotePlugin).toBeDefined(); }); it('enables its GActions when connected', function () { testRig.setConnected(true); expect(localPlugin.device.get_action_enabled('clipboardPush')).toBeTrue(); expect(localPlugin.device.get_action_enabled('clipboardPull')).toBeTrue(); expect(remotePlugin.device.get_action_enabled('clipboardPush')).toBeTrue(); expect(remotePlugin.device.get_action_enabled('clipboardPull')).toBeTrue(); }); it('sends initial clipboard content when connected', async function () { // Prime the clipboard and simulate a new connection localPlugin._localBuffer = localPlugin._clipboard.text; localPlugin._localTimestamp = Date.now(); localPlugin.settings.set_boolean('send-content', true); localPlugin.connected(); await remotePlugin.awaitPacket('kdeconnect.clipboard.connect'); expect(remotePlugin._remoteBuffer).toBe('initial'); localPlugin.settings.set_boolean('send-content', false); }); it('will not push content when not allowed', function () { localPlugin._clipboard.text = 'foo'; expect(localPlugin.device.sendPacket).not.toHaveBeenCalled(); }); it('will push content when allowed', async function () { localPlugin.settings.set_boolean('send-content', true); localPlugin._clipboard.text = 'bar'; await remotePlugin.awaitPacket('kdeconnect.clipboard'); expect(remotePlugin._remoteBuffer).toBe('bar'); }); it('will not pull content when not allowed', async function () { localPlugin._clipboard.text = 'baz'; await remotePlugin.awaitPacket('kdeconnect.clipboard'); expect(remotePlugin._clipboard.text).not.toBe('baz'); }); it('will pull content when allowed', async function () { remotePlugin.settings.set_boolean('receive-content', true); localPlugin._clipboard.text = 'qux'; await remotePlugin.awaitPacket('kdeconnect.clipboard'); expect(remotePlugin._clipboard.text).toBe('qux'); }); it('disables its GActions when disconnected', function () { testRig.setConnected(false); expect(localPlugin.device.get_action_enabled('clipboardPush')).toBeFalse(); expect(localPlugin.device.get_action_enabled('clipboardPull')).toBeFalse(); expect(remotePlugin.device.get_action_enabled('clipboardPush')).toBeFalse(); expect(remotePlugin.device.get_action_enabled('clipboardPull')).toBeFalse(); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/testContactsPlugin.js000066400000000000000000000102631421543444100320200ustar00rootroot00000000000000'use strict'; const Utils = imports.fixtures.utils; const GLib = imports.gi.GLib; const VCards = { valid: Utils.loadDataContents('vcard-valid.vcf'), invalid: Utils.loadDataContents('vcard-invalid.vcf'), }; const Packets = { uidsResponse: { type: 'kdeconnect.contacts.response_uids_timestamps', body: { uids: ['valid', 'invalid'], valid: Date.now() + 10, invalid: Date.now() + 20, }, }, vcardsResponse: { type: 'kdeconnect.contacts.response_vcards', body: { uids: ['valid', 'invalid'], valid: VCards.valid, invalid: VCards.invalid, }, }, }; function handlePacket(packet) { if (packet.type === 'kdeconnect.contacts.request_all_uids_timestamps') { this.sendPacket(Packets.uidsResponse); } else if (packet.type === 'kdeconnect.contacts.request_vcards_by_uid') { const response = Packets.vcardsResponse; response.body = {uids: packet.body.uids}; for (const uid of response.body.uids) response.body[uid] = VCards[uid]; this.sendPacket(response); } } describe('The contacts plugin', function () { let testRig; let localPlugin; let remoteDevice; beforeAll(async function () { testRig = new Utils.TestRig(); await testRig.prepare({ localDevice: { incomingCapabilities: [ 'kdeconnect.contacts.request_all_uids_timestamps', 'kdeconnect.contacts.request_vcards_by_uid', ], outgoingCapabilities: [ 'kdeconnect.contacts.response_uids_timestamps', 'kdeconnect.contacts.response_vcards', ], }, remoteDevice: { incomingCapabilities: [ 'kdeconnect.contacts.response_uids_timestamps', 'kdeconnect.contacts.response_vcards', ], outgoingCapabilities: [ 'kdeconnect.contacts.request_all_uids_timestamps', 'kdeconnect.contacts.request_vcards_by_uid', ], }, }); testRig.setPaired(true); remoteDevice = testRig.remoteDevice; remoteDevice.handlePacket = handlePacket.bind(remoteDevice); }); afterAll(function () { testRig.destroy(); }); beforeEach(function () { if (localPlugin) spyOn(localPlugin, 'handlePacket').and.callThrough(); }); it('can be loaded', async function () { await testRig.loadPlugins(); localPlugin = testRig.localDevice._plugins.get('contacts'); expect(localPlugin).toBeDefined(); }); it('requests contacts when connected', async function () { GLib.test_expect_message('libebook-contacts', GLib.LogLevelFlags.LEVEL_WARNING, '*'); testRig.setConnected(true); await localPlugin.awaitPacket('kdeconnect.contacts.response_vcards'); expect(localPlugin._store.get_contact('valid')).toBeDefined(); }); it('clears the cache when requested', async function () { localPlugin.clearCache(); // TODO: this seems to indicate we're deferring too much while (localPlugin._store.contacts.length) await Promise.idle(); expect(localPlugin._store.contacts.length).toEqual(0); }); it('handles and stores vCards (EBookContacts)', async function () { localPlugin._requestVCards(['valid']); await localPlugin.awaitPacket('kdeconnect.contacts.response_vcards'); expect(localPlugin._store.get_contact('valid')).toBeDefined(); }); it('handles and stores vCards (native)', async function () { localPlugin.clearCache(); // TODO: this seems to indicate we're deferring too much while (localPlugin._store.contacts.length) await Promise.idle(); imports.service.plugins.contacts.EBookContacts = null; localPlugin._requestVCards(['valid']); await localPlugin.awaitPacket('kdeconnect.contacts.response_vcards'); expect(localPlugin._store.get_contact('valid')).toBeDefined(); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/testFindmyphonePlugin.js000066400000000000000000000044311421543444100325220ustar00rootroot00000000000000'use strict'; const Utils = imports.fixtures.utils; describe('The findmyphone plugin', function () { let testRig; let localPlugin, remotePlugin; beforeAll(async function () { Utils.mockComponents(); testRig = new Utils.TestRig(); await testRig.prepare({ localDevice: { incomingCapabilities: ['kdeconnect.findmyphone.request'], outgoingCapabilities: ['kdeconnect.findmyphone.request'], }, remoteDevice: { incomingCapabilities: ['kdeconnect.findmyphone.request'], outgoingCapabilities: ['kdeconnect.findmyphone.request'], }, }); testRig.setPaired(true); }); afterAll(function () { testRig.destroy(); }); beforeEach(function () { if (localPlugin && remotePlugin) { spyOn(remotePlugin, 'handlePacket').and.callThrough(); spyOn(remotePlugin, '_handleRequest'); } }); it('can be loaded', async function () { await testRig.loadPlugins(); localPlugin = testRig.localDevice._plugins.get('findmyphone'); remotePlugin = testRig.remoteDevice._plugins.get('findmyphone'); expect(localPlugin).toBeDefined(); expect(remotePlugin).toBeDefined(); }); it('enables its GActions when connected', function () { testRig.setConnected(true); expect(localPlugin.device.get_action_enabled('ring')).toBeTrue(); expect(remotePlugin.device.get_action_enabled('ring')).toBeTrue(); }); it('can send and receive ring requests', async function () { localPlugin.ring(); await remotePlugin.awaitPacket('kdeconnect.findmyphone.request'); expect(remotePlugin._handleRequest).toHaveBeenCalled(); }); it('stops ringing on the second request', async function () { localPlugin.ring(); await remotePlugin.awaitPacket('kdeconnect.findmyphone.request'); expect(remotePlugin._handleRequest).toHaveBeenCalled(); }); it('disables its GActions when disconnected', function () { testRig.setConnected(false); expect(localPlugin.device.get_action_enabled('ring')).toBeFalse(); expect(remotePlugin.device.get_action_enabled('ring')).toBeFalse(); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/testMousepadPlugin.js000066400000000000000000000235251421543444100320240ustar00rootroot00000000000000'use strict'; const Utils = imports.fixtures.utils; const {Gdk} = imports.gi; describe('The mousepad plugin', function () { let testRig; let localPlugin, remotePlugin; beforeAll(async function () { Utils.mockComponents(); testRig = new Utils.TestRig(); await testRig.prepare({ localDevice: { incomingCapabilities: [ 'kdeconnect.mousepad.echo', 'kdeconnect.mousepad.keyboardstate', 'kdeconnect.mousepad.request', ], outgoingCapabilities: [ 'kdeconnect.mousepad.echo', 'kdeconnect.mousepad.keyboardstate', 'kdeconnect.mousepad.request', ], }, remoteDevice: { incomingCapabilities: [ 'kdeconnect.mousepad.echo', 'kdeconnect.mousepad.keyboardstate', 'kdeconnect.mousepad.request', ], outgoingCapabilities: [ 'kdeconnect.mousepad.echo', 'kdeconnect.mousepad.keyboardstate', 'kdeconnect.mousepad.request', ], }, }); testRig.setPaired(true); }); afterAll(function () { testRig.destroy(); }); beforeEach(function () { if (localPlugin && remotePlugin) { spyOn(localPlugin, 'handlePacket').and.callThrough(); spyOn(localPlugin, '_handleEcho').and.callThrough(); spyOn(remotePlugin, 'handlePacket').and.callThrough(); spyOn(remotePlugin, '_sendEcho').and.callThrough(); spyOn(remotePlugin._input, 'clickPointer'); spyOn(remotePlugin._input, 'doubleclickPointer'); spyOn(remotePlugin._input, 'pressPointer'); spyOn(remotePlugin._input, 'releasePointer'); spyOn(remotePlugin._input, 'movePointer'); spyOn(remotePlugin._input, 'scrollPointer'); spyOn(remotePlugin._input, 'pressKeys'); } }); it('can be loaded', async function () { await testRig.loadPlugins(); localPlugin = testRig.localDevice._plugins.get('mousepad'); remotePlugin = testRig.remoteDevice._plugins.get('mousepad'); expect(localPlugin).toBeDefined(); expect(remotePlugin).toBeDefined(); }); it('enables its GActions when connected', function () { testRig.setConnected(true); expect(localPlugin.device.get_action_enabled('keyboard')).toBeTrue(); expect(remotePlugin.device.get_action_enabled('keyboard')).toBeTrue(); }); it('sends its keyboard state when connected', async function () { const localState = localPlugin.settings.get_boolean('share-control'); localPlugin.connected(); await remotePlugin.awaitPacket('kdeconnect.mousepad.keyboardstate'); expect(remotePlugin.state).toBe(localState); }); it('sends its keyboard state when changed', async function () { const previousState = localPlugin.settings.get_boolean('share-control'); localPlugin.settings.set_boolean('share-control', !previousState); await remotePlugin.awaitPacket('kdeconnect.mousepad.keyboardstate'); expect(remotePlugin.state).toBe(!previousState); }); describe('handles keypresses', function () { const keyModifiers = Gdk.ModifierType.MOD1_MASK | Gdk.ModifierType.SHIFT_MASK; const specialModifiers = Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SUPER_MASK; it('without modifiers', async function () { localPlugin.device.sendPacket({ type: 'kdeconnect.mousepad.request', body: {key: 'a'}, }); await remotePlugin.awaitPacket('kdeconnect.mousepad.request'); expect(remotePlugin._input.pressKeys).toHaveBeenCalledWith('a', 0); }); it('with modifiers', async function () { localPlugin.device.sendPacket({ type: 'kdeconnect.mousepad.request', body: { key: 'b', alt: true, ctrl: false, shift: true, super: false, }, }); await remotePlugin.awaitPacket('kdeconnect.mousepad.request'); expect(remotePlugin._input.pressKeys).toHaveBeenCalledWith('b', keyModifiers); }); it('for special keys without modifiers', async function () { localPlugin.device.sendPacket({ type: 'kdeconnect.mousepad.request', body: {specialKey: 1}, }); await remotePlugin.awaitPacket('kdeconnect.mousepad.request'); expect(remotePlugin._input.pressKeys).toHaveBeenCalledWith( Gdk.KEY_BackSpace, 0); }); it('for special keys with modifiers', async function () { localPlugin.device.sendPacket({ type: 'kdeconnect.mousepad.request', body: { specialKey: 2, alt: false, ctrl: true, shift: false, super: true, }, }); await remotePlugin.awaitPacket('kdeconnect.mousepad.request'); expect(remotePlugin._input.pressKeys).toHaveBeenCalledWith( Gdk.KEY_Tab, specialModifiers); }); it('and sends an acknowledging packet', async function () { localPlugin.device.sendPacket({ type: 'kdeconnect.mousepad.request', body: { key: 'c', sendAck: true, }, }); await remotePlugin.awaitPacket('kdeconnect.mousepad.request'); expect(remotePlugin._sendEcho).toHaveBeenCalled(); await localPlugin.awaitPacket('kdeconnect.mousepad.echo'); expect(localPlugin._handleEcho).toHaveBeenCalledWith({ key: 'c', isAck: true, }); }); }); describe('handles pointer events', function () { it('for movement', async function () { localPlugin.device.sendPacket({ type: 'kdeconnect.mousepad.request', body: { dx: 1, dy: -1, }, }); await remotePlugin.awaitPacket('kdeconnect.mousepad.request'); expect(remotePlugin._input.movePointer).toHaveBeenCalledWith(1, -1); }); it('for scrolling', async function () { localPlugin.device.sendPacket({ type: 'kdeconnect.mousepad.request', body: { dx: 0, dy: 1, scroll: true, }, }); await remotePlugin.awaitPacket('kdeconnect.mousepad.request'); expect(remotePlugin._input.scrollPointer).toHaveBeenCalledWith(0, 1); }); it('for left clicks', async function () { localPlugin.device.sendPacket({ type: 'kdeconnect.mousepad.request', body: {singleclick: true}, }); await remotePlugin.awaitPacket('kdeconnect.mousepad.request'); expect(remotePlugin._input.clickPointer).toHaveBeenCalledWith( Gdk.BUTTON_PRIMARY); }); it('for middle clicks', async function () { localPlugin.device.sendPacket({ type: 'kdeconnect.mousepad.request', body: {middleclick: true}, }); await remotePlugin.awaitPacket('kdeconnect.mousepad.request'); expect(remotePlugin._input.clickPointer).toHaveBeenCalledWith( Gdk.BUTTON_MIDDLE); }); it('for right clicks', async function () { localPlugin.device.sendPacket({ type: 'kdeconnect.mousepad.request', body: {rightclick: true}, }); await remotePlugin.awaitPacket('kdeconnect.mousepad.request'); expect(remotePlugin._input.clickPointer).toHaveBeenCalledWith( Gdk.BUTTON_SECONDARY); }); it('for double clicks', async function () { localPlugin.device.sendPacket({ type: 'kdeconnect.mousepad.request', body: {doubleclick: true}, }); await remotePlugin.awaitPacket('kdeconnect.mousepad.request'); expect(remotePlugin._input.doubleclickPointer).toHaveBeenCalledWith( Gdk.BUTTON_PRIMARY); }); it('for button presses', async function () { localPlugin.device.sendPacket({ type: 'kdeconnect.mousepad.request', body: {singlehold: true}, }); await remotePlugin.awaitPacket('kdeconnect.mousepad.request'); expect(remotePlugin._input.pressPointer).toHaveBeenCalledWith( Gdk.BUTTON_PRIMARY); }); it('for button releases', async function () { localPlugin.device.sendPacket({ type: 'kdeconnect.mousepad.request', body: {singlerelease: true}, }); await remotePlugin.awaitPacket('kdeconnect.mousepad.request'); expect(remotePlugin._input.releasePointer).toHaveBeenCalledWith( Gdk.BUTTON_PRIMARY); }); }); // TODO it('ignores input events when not allowed', function () { expect(true).toBeTrue(); }); it('disables its GActions when disconnected', function () { testRig.setConnected(false); expect(localPlugin.device.get_action_enabled('keyboard')).toBeFalse(); expect(remotePlugin.device.get_action_enabled('keyboard')).toBeFalse(); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/testMprisPlugin.js000066400000000000000000000232621421543444100313370ustar00rootroot00000000000000'use strict'; const Utils = imports.fixtures.utils; describe('The mpris plugin', function () { let testRig; let localPlugin, remotePlugin; beforeAll(async function () { Utils.mockComponents(); testRig = new Utils.TestRig(); await testRig.prepare({ localDevice: { incomingCapabilities: [ 'kdeconnect.mpris', 'kdeconnect.mpris.request', ], outgoingCapabilities: [ 'kdeconnect.mpris', 'kdeconnect.mpris.request', ], }, remoteDevice: { incomingCapabilities: [ 'kdeconnect.mpris', 'kdeconnect.mpris.request', ], outgoingCapabilities: [ 'kdeconnect.mpris', 'kdeconnect.mpris.request', ], }, }); testRig.setPaired(true); }); afterAll(function () { testRig.destroy(); }); beforeEach(function () { if (localPlugin && remotePlugin) { spyOn(localPlugin, 'handlePacket').and.callThrough(); spyOn(remotePlugin, 'handlePacket').and.callThrough(); } }); it('can be loaded', async function () { await testRig.loadPlugins(); localPlugin = testRig.localDevice._plugins.get('mpris'); remotePlugin = testRig.remoteDevice._plugins.get('mpris'); expect(localPlugin).toBeDefined(); expect(remotePlugin).toBeDefined(); }); it('requests and reports players when connected', async function () { await testRig.setConnected(true); await Promise.all([ localPlugin.awaitPacket('kdeconnect.mpris.request'), remotePlugin.awaitPacket('kdeconnect.mpris.request'), localPlugin.awaitPacket('kdeconnect.mpris'), remotePlugin.awaitPacket('kdeconnect.mpris'), ]); }); it('adds players', async function () { localPlugin._mpris.addPlayer('Music Player'); expect(localPlugin._mpris.hasPlayer('Music Player')).toBeTrue(); await remotePlugin.awaitPacket('kdeconnect.mpris', { playerList: ['Music Player'], }); await remotePlugin.awaitPacket('kdeconnect.mpris', { player: 'Music Player', canGoNext: false, canGoPrevious: false, canPause: false, canPlay: false, canSeek: false, isPlaying: false, length: 0, pos: 0, volume: 100, }); expect(remotePlugin._players.has('Music Player')).toBeTrue(); }); it('sends and handles player changes', async function () { const localPlayer = localPlugin._mpris.getPlayer('Music Player'); const remotePlayer = remotePlugin._players.get('Music Player'); spyOn(remotePlayer, 'export'); // Update while accounting for the position/length/volume conversion localPlayer.update({ CanGoNext: true, CanGoPrevious: true, CanPause: true, CanPlay: true, CanSeek: true, PlaybackStatus: 'Playing', Position: 50000000, Volume: 0.5, Metadata: { 'xesam:artist': ['Some Artist'], 'xesam:album': 'Some Album', 'xesam:title': 'Track 1', 'mpris:length': 100000000, }, }); await remotePlugin.awaitPacket('kdeconnect.mpris', { player: 'Music Player', canGoNext: true, canGoPrevious: true, canPause: true, canPlay: true, canSeek: true, isPlaying: true, length: 100000, pos: 50000, volume: 50, artist: 'Some Artist', album: 'Some Album', title: 'Track 1', nowPlaying: 'Some Artist - Track 1', }); expect(remotePlayer.PlaybackStatus).toBe('Playing'); expect(remotePlayer.export).toHaveBeenCalled(); }); it('sends and handles player seeking', async function () { const localPlayer = localPlugin._mpris.getPlayer('Music Player'); const remotePlayer = remotePlugin._players.get('Music Player'); // Update while accounting for the offset conversion localPlayer.Seek(100000); // NOTE: although we can handle full seeked signals, kdeconnect-android // does not, and expects a position update instead await remotePlugin.awaitPacket('kdeconnect.mpris', { player: 'Music Player', pos: 50100, // Seek: 100, }); expect(remotePlayer.Position).toBe(50100000); }); it('sends and handles action commands', async function () { const localPlayer = localPlugin._mpris.getPlayer('Music Player'); const remotePlayer = remotePlugin._players.get('Music Player'); // Pause remotePlayer.Pause(); await localPlugin.awaitPacket('kdeconnect.mpris.request', { player: 'Music Player', action: 'Pause', }); expect(localPlayer.PlaybackStatus).toBe('Paused'); // Play remotePlayer.Play(); await localPlugin.awaitPacket('kdeconnect.mpris.request', { player: 'Music Player', action: 'Play', }); expect(localPlayer.PlaybackStatus).toBe('Playing'); // Play/Pause remotePlayer.PlayPause(); await localPlugin.awaitPacket('kdeconnect.mpris.request', { player: 'Music Player', action: 'PlayPause', }); expect(localPlayer.PlaybackStatus).toBe('Paused'); // Next remotePlayer.Next(); await localPlugin.awaitPacket('kdeconnect.mpris.request', { player: 'Music Player', action: 'Next', }); expect(localPlayer.Metadata['xesam:title']).toBe('Track 2'); // Previous remotePlayer.Previous(); await localPlugin.awaitPacket('kdeconnect.mpris.request', { player: 'Music Player', action: 'Previous', }); expect(localPlayer.Metadata['xesam:title']).toBe('Track 1'); // Stop remotePlayer.Stop(); await localPlugin.awaitPacket('kdeconnect.mpris.request', { player: 'Music Player', action: 'Stop', }); expect(localPlayer.PlaybackStatus).toBe('Stopped'); }); it('sends and receives album art', async function () { pending('FIXME'); const localPlayer = localPlugin._mpris.getPlayer('Music Player'); const remotePlayer = remotePlugin._players.get('Music Player'); const localUrl = Utils.getDataUri('album.png'); localPlayer.update({ player: 'Music Player', Metadata: { 'xesam:artist': ['Some Artist'], 'xesam:album': 'Some Album', 'xesam:title': 'Track 1', 'mpris:length': 100000000, 'mpris:artUrl': localUrl, }, }); await remotePlugin.awaitPacket('kdeconnect.mpris', { player: 'Music Player', albumArtUrl: localUrl, }); await new Promise((resolve, reject) => { remotePlayer.connect('notify::Metadata', () => { resolve(); }); }); // Wait for the album art to transfer const remoteUrl = remotePlayer._getFile(localUrl).get_uri(); const playerUrl = remotePlayer.Metadata['mpris:artUrl'].unpack(); expect(playerUrl).toBe(remoteUrl); }); it('unexports players when they can not be controlled', async function () { const localPlayer = localPlugin._mpris.getPlayer('Music Player'); const remotePlayer = remotePlugin._players.get('Music Player'); spyOn(remotePlayer, 'unexport'); localPlayer.update({ CanGoNext: false, CanGoPrevious: false, CanPause: false, CanPlay: false, CanSeek: false, }); await remotePlugin.awaitPacket('kdeconnect.mpris', { player: 'Music Player', canGoNext: false, canGoPrevious: false, canPause: false, canPlay: false, canSeek: false, }); expect(remotePlayer.unexport).toHaveBeenCalled(); }); it('exports players when they can be controlled', async function () { const localPlayer = localPlugin._mpris.getPlayer('Music Player'); const remotePlayer = remotePlugin._players.get('Music Player'); spyOn(remotePlayer, 'export'); localPlayer.update({ CanGoNext: true, CanGoPrevious: true, CanPause: true, CanPlay: true, CanSeek: true, }); await remotePlugin.awaitPacket('kdeconnect.mpris', { player: 'Music Player', canGoNext: true, canGoPrevious: true, canPause: true, canPlay: true, canSeek: true, }); expect(remotePlayer.export).toHaveBeenCalled(); }); it('removes players', async function () { localPlugin._mpris.removePlayer('Music Player'); expect(localPlugin._mpris.hasPlayer('Music Player')).toBeFalse(); await remotePlugin.awaitPacket('kdeconnect.mpris', { playerList: [], }); expect(remotePlugin._players.has('Music Player')).toBeFalse(); }); it('disables its GActions when disconnected', async function () { await testRig.setConnected(false); expect(true).toBeTrue(); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/testNotificationPlugin.js000066400000000000000000000232361421543444100326740ustar00rootroot00000000000000'use strict'; const {Gio, GLib} = imports.gi; const Utils = imports.fixtures.utils; const Notifications = { withoutIcon: { appName: 'Application', id: 'test-notification', title: 'Notification Title', text: 'Notification Body', ticker: 'Notification Title - Notification Body', time: '1599103247103', isClearable: true, }, withIcon: { appName: 'Application', id: 'test-notification', title: 'Notification Title', text: 'Notification Body', ticker: 'Notification Title - Notification Body', time: '1599103247103', isClearable: true, icon: new Gio.FileIcon({ file: Gio.File.new_for_uri(Utils.getDataUri('album.png')), }), }, repliable: { appName: 'Application', id: 'test-notification', title: 'Notification Title', text: 'Notification Body', ticker: 'Notification Title - Notification Body', time: '1599103247103', isClearable: true, requestReplyId: GLib.uuid_string_random(), }, actionable: { appName: 'Application', id: 'test-notification', title: 'Notification Title', text: 'Notification Body', ticker: 'Notification Title - Notification Body', time: '1599103247103', isClearable: true, actions: ['One', 'Two', 'Three'], }, }; describe('The notification plugin', function () { let testRig; let localPlugin, remotePlugin; beforeAll(async function () { Utils.mockComponents(); testRig = new Utils.TestRig(); await testRig.prepare({ localDevice: { incomingCapabilities: [ 'kdeconnect.notification', 'kdeconnect.notification.action', 'kdeconnect.notification.reply', 'kdeconnect.notification.request', ], outgoingCapabilities: [ 'kdeconnect.notification', 'kdeconnect.notification.action', 'kdeconnect.notification.reply', 'kdeconnect.notification.request', ], }, remoteDevice: { incomingCapabilities: [ 'kdeconnect.notification', 'kdeconnect.notification.action', 'kdeconnect.notification.reply', 'kdeconnect.notification.request', ], outgoingCapabilities: [ 'kdeconnect.notification', 'kdeconnect.notification.action', 'kdeconnect.notification.reply', 'kdeconnect.notification.request', ], }, }); testRig.setPaired(true); }); afterAll(function () { testRig.destroy(); }); beforeEach(function () { if (localPlugin && remotePlugin) { spyOn(localPlugin, 'handlePacket').and.callThrough(); spyOn(localPlugin.device, 'hideNotification'); spyOn(localPlugin.device, 'showNotification'); spyOn(remotePlugin, 'handlePacket').and.callThrough(); spyOn(remotePlugin.device, 'hideNotification'); spyOn(remotePlugin.device, 'showNotification'); } }); it('can be loaded', async function () { await testRig.loadPlugins(); localPlugin = testRig.localDevice._plugins.get('notification'); remotePlugin = testRig.remoteDevice._plugins.get('notification'); expect(localPlugin).toBeDefined(); expect(remotePlugin).toBeDefined(); }); it('enables its GActions when connected', function () { testRig.setConnected(true); for (const action in localPlugin._meta.actions) expect(localPlugin.device.get_action_enabled(action)).toBeTrue(); }); it('request notifications when connected', async function () { spyOn(remotePlugin, '_handleNotificationRequest'); localPlugin.connected(); await remotePlugin.awaitPacket('kdeconnect.notification.request', { request: true, }); expect(remotePlugin._handleNotificationRequest).toHaveBeenCalled(); }); describe('can send and receive notifications', function () { it('without icons', async function () { localPlugin._listener.fakeNotification(Notifications.withoutIcon); await remotePlugin.awaitPacket('kdeconnect.notification', Notifications.withoutIcon); expect(remotePlugin.device.showNotification).toHaveBeenCalled(); }); it('with icons', async function () { localPlugin._listener.fakeNotification(Notifications.withIcon); await remotePlugin.awaitPacket('kdeconnect.notification', Notifications.withoutIcon); // while (!remotePlugin.device.showNotification.calls.any()) // await Promise.idle(); // expect(remotePlugin.device.showNotification).toHaveBeenCalled(); }); }); describe('ignores notifications', function () { beforeEach(function () { spyOn(localPlugin.device, 'sendPacket').and.callThrough(); }); it('when sending is not allowed', function () { localPlugin.settings.set_boolean('send-notifications', false); localPlugin._listener.fakeNotification(Notifications.withoutIcon); expect(localPlugin.device.sendPacket).not.toHaveBeenCalled(); localPlugin.settings.set_boolean('send-notifications', true); }); it('when sending in an active session is not allowed', function () { localPlugin.settings.set_boolean('send-active', false); localPlugin._listener.fakeNotification(Notifications.withoutIcon); expect(localPlugin.device.sendPacket).not.toHaveBeenCalled(); localPlugin.settings.set_boolean('send-active', true); }); it('when sending for the application is not allowed', function () { const applications = localPlugin.settings.get_string('applications'); const disabled = JSON.parse(applications); disabled['Application'].enabled = false; localPlugin.settings.set_string('applications', JSON.stringify(disabled)); localPlugin._listener.fakeNotification(Notifications.withoutIcon); expect(localPlugin.device.sendPacket).not.toHaveBeenCalled(); localPlugin.settings.set_string('applications', applications); }); }); it('can handle repliable notifications', async function () { // Ensure the packet sends... localPlugin._listener.fakeNotification(Notifications.repliable); await remotePlugin.awaitPacket('kdeconnect.notification', Notifications.repliable); expect(remotePlugin.device.showNotification).toHaveBeenCalled(); // ...then check the notification was properly formed const invocation = remotePlugin.device.showNotification.calls.first(); const notif = invocation.args[0]; expect(notif.action.name).toBe('replyNotification'); }); it('can send replies for repliable notifications', function () { spyOn(localPlugin.device, 'sendPacket'); const uuid = GLib.uuid_string_random(); const message = 'message'; localPlugin.replyNotification(uuid, message, {}); expect(localPlugin.device.sendPacket).toHaveBeenCalled(); }); it('can handle notifications with actions', async function () { // Ensure the packet sends... localPlugin._listener.fakeNotification(Notifications.actionable); await remotePlugin.awaitPacket('kdeconnect.notification', Notifications.actionable); expect(remotePlugin.device.showNotification).toHaveBeenCalled(); // ...then check the notification was properly formed const invocation = remotePlugin.device.showNotification.calls.first(); const notif = invocation.args[0]; expect(notif.buttons[0].label).toBe('One'); expect(notif.buttons[1].label).toBe('Two'); expect(notif.buttons[2].label).toBe('Three'); }); it('can activate actions for notifications', function () { spyOn(localPlugin.device, 'sendPacket'); const id = GLib.uuid_string_random(); const action = 'Action'; localPlugin.activateNotification(id, action); expect(localPlugin.device.sendPacket).toHaveBeenCalled(); }); it('can withdraw local notifications', async function () { const id = GLib.uuid_string_random(); localPlugin.withdrawNotification(id); await remotePlugin.awaitPacket('kdeconnect.notification', { id: id, isCancel: true, }); expect(remotePlugin.device.hideNotification).toHaveBeenCalledWith(id); }); it('can close remote notifications', async function () { spyOn(remotePlugin, '_handleNotificationRequest'); const id = GLib.uuid_string_random(); localPlugin.closeNotification(id); await remotePlugin.awaitPacket('kdeconnect.notification.request', { cancel: id, }); expect(remotePlugin._handleNotificationRequest).toHaveBeenCalled(); }); it('disables its GActions when disconnected', function () { testRig.setConnected(false); for (const action in localPlugin._meta.actions) expect(localPlugin.device.get_action_enabled(action)).toBeFalse(); for (const action in remotePlugin._meta.actions) expect(remotePlugin.device.get_action_enabled(action)).toBeFalse(); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/testPhotoPlugin.js000066400000000000000000000045121421543444100313330ustar00rootroot00000000000000'use strict'; const Utils = imports.fixtures.utils; describe('The photo plugin', function () { let testRig; let localPlugin, remotePlugin; beforeAll(async function () { testRig = new Utils.TestRig(); await testRig.prepare({ localDevice: { incomingCapabilities: [ 'kdeconnect.photo', 'kdeconnect.photo.request', ], outgoingCapabilities: [ 'kdeconnect.photo', 'kdeconnect.photo.request', ], }, remoteDevice: { incomingCapabilities: [ 'kdeconnect.photo', 'kdeconnect.photo.request', ], outgoingCapabilities: [ 'kdeconnect.photo', 'kdeconnect.photo.request', ], }, }); testRig.setPaired(true); }); afterAll(function () { testRig.destroy(); }); beforeEach(function () { if (localPlugin && remotePlugin) { spyOn(localPlugin, 'handlePacket').and.callThrough(); spyOn(remotePlugin, 'handlePacket').and.callThrough(); } }); it('can be loaded', async function () { await testRig.loadPlugins(); localPlugin = testRig.localDevice._plugins.get('photo'); remotePlugin = testRig.remoteDevice._plugins.get('photo'); expect(localPlugin).toBeDefined(); expect(remotePlugin).toBeDefined(); }); it('enables its GActions when connected', function () { testRig.setConnected(true); expect(localPlugin.device.get_action_enabled('photo')).toBeTrue(); expect(remotePlugin.device.get_action_enabled('photo')).toBeTrue(); }); it('can request and receive photos', async function () { spyOn(remotePlugin, '_sendPhoto'); localPlugin.photo(); await remotePlugin.awaitPacket('kdeconnect.photo.request'); expect(remotePlugin._sendPhoto).toHaveBeenCalled(); }); it('disables its GActions when disconnected', function () { testRig.setConnected(false); expect(localPlugin.device.get_action_enabled('photo')).toBeFalse(); expect(remotePlugin.device.get_action_enabled('photo')).toBeFalse(); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/testPingPlugin.js000066400000000000000000000037401421543444100311410ustar00rootroot00000000000000'use strict'; const Utils = imports.fixtures.utils; describe('The ping plugin', function () { let testRig; let localPlugin, remotePlugin; beforeAll(async function () { testRig = new Utils.TestRig(); await testRig.prepare({ localDevice: { incomingCapabilities: ['kdeconnect.ping'], outgoingCapabilities: ['kdeconnect.ping'], }, remoteDevice: { incomingCapabilities: ['kdeconnect.ping'], outgoingCapabilities: ['kdeconnect.ping'], }, }); testRig.setPaired(true); }); afterAll(function () { testRig.destroy(); }); beforeEach(function () { if (remotePlugin) { spyOn(remotePlugin, 'handlePacket').and.callThrough(); spyOn(remotePlugin.device, 'showNotification'); } }); it('can be loaded', async function () { await testRig.loadPlugins(); localPlugin = testRig.localDevice._plugins.get('ping'); remotePlugin = testRig.remoteDevice._plugins.get('ping'); expect(localPlugin).toBeDefined(); expect(remotePlugin).toBeDefined(); }); it('enables its GActions when connected', function () { testRig.setConnected(true); expect(localPlugin.device.get_action_enabled('ping')).toBeTrue(); expect(remotePlugin.device.get_action_enabled('ping')).toBeTrue(); }); it('can send and receive pings', async function () { localPlugin.ping(); await remotePlugin.awaitPacket('kdeconnect.ping'); expect(remotePlugin.handlePacket).toHaveBeenCalled(); expect(testRig.remoteDevice.showNotification).toHaveBeenCalled(); }); it('disables its GActions when disconnected', function () { testRig.setConnected(false); expect(localPlugin.device.get_action_enabled('ping')).toBeFalse(); expect(remotePlugin.device.get_action_enabled('ping')).toBeFalse(); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/testPresenterPlugin.js000066400000000000000000000035341421543444100322140ustar00rootroot00000000000000'use strict'; const Utils = imports.fixtures.utils; describe('The presenter plugin', function () { let testRig; let localPlugin, remotePlugin; beforeAll(async function () { testRig = new Utils.TestRig(); await testRig.prepare({ localDevice: { incomingCapabilities: [ 'kdeconnect.presenter', ], outgoingCapabilities: [ 'kdeconnect.presenter', ], }, remoteDevice: { incomingCapabilities: [ 'kdeconnect.presenter', ], outgoingCapabilities: [ 'kdeconnect.presenter', ], }, }); testRig.setPaired(true); testRig.setConnected(true); }); afterAll(function () { testRig.destroy(); }); beforeEach(function () { if (remotePlugin) { spyOn(remotePlugin, 'handlePacket').and.callThrough(); spyOn(remotePlugin._input, 'movePointer'); } }); it('can be loaded', async function () { await testRig.loadPlugins(); localPlugin = testRig.localDevice._plugins.get('presenter'); remotePlugin = testRig.remoteDevice._plugins.get('presenter'); expect(localPlugin).toBeDefined(); expect(remotePlugin).toBeDefined(); }); it('can receive presentation commands', async function () { localPlugin.device.sendPacket({ type: 'kdeconnect.presenter', body: { dx: 0.1, dy: 0.1, }, }); await remotePlugin.awaitPacket('kdeconnect.presenter', { dx: 0.1, dy: 0.1, }); expect(remotePlugin._input.movePointer).toHaveBeenCalledWith(100, 100); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/testRuncommandPlugin.js000066400000000000000000000067301421543444100323510ustar00rootroot00000000000000'use strict'; const {GLib} = imports.gi; const Utils = imports.fixtures.utils; describe('The runcommand plugin', function () { let testRig; let localPlugin, remotePlugin; beforeAll(async function () { testRig = new Utils.TestRig(); await testRig.prepare({ localDevice: { incomingCapabilities: [ 'kdeconnect.runcommand', 'kdeconnect.runcommand.request', ], outgoingCapabilities: [ 'kdeconnect.runcommand', 'kdeconnect.runcommand.request', ], }, remoteDevice: { incomingCapabilities: [ 'kdeconnect.runcommand', 'kdeconnect.runcommand.request', ], outgoingCapabilities: [ 'kdeconnect.runcommand', 'kdeconnect.runcommand.request', ], }, }); testRig.setPaired(true); }); afterAll(function () { testRig.destroy(); }); beforeEach(function () { if (localPlugin && remotePlugin) { spyOn(localPlugin, 'handlePacket').and.callThrough(); spyOn(remotePlugin, 'handlePacket').and.callThrough(); } }); it('can be loaded', async function () { await testRig.loadPlugins(); localPlugin = testRig.localDevice._plugins.get('runcommand'); remotePlugin = testRig.remoteDevice._plugins.get('runcommand'); expect(localPlugin).toBeDefined(); expect(remotePlugin).toBeDefined(); }); it('enables its GActions when connected', function () { testRig.setConnected(true); expect(localPlugin.device.get_action_enabled('executeCommand')).toBeTrue(); expect(remotePlugin.device.get_action_enabled('executeCommand')).toBeTrue(); }); it('sends and request the list of commands when connected', async function () { localPlugin.connected(); await remotePlugin.awaitPacket('kdeconnect.runcommand.request', { requestCommandList: true, }); await remotePlugin.awaitPacket('kdeconnect.runcommand', { commandList: {}, }); }); it('sends the command list when it changes', async function () { const commandList = new GLib.Variant('a{sv}', { 'command-uuid': new GLib.Variant('a{ss}', { name: 'Test Command', command: 'ls', }), }); localPlugin.settings.set_value('command-list', commandList); await remotePlugin.awaitPacket('kdeconnect.runcommand', { commandList: '{"command-uuid":{"name":"Test Command","command":"ls"}}', }); expect(remotePlugin.remote_commands['command-uuid']).toBeDefined(); }); it('can activate a remote command', async function () { spyOn(localPlugin.device, 'launchProcess'); remotePlugin.executeCommand('command-uuid'); await localPlugin.awaitPacket('kdeconnect.runcommand.request', { key: 'command-uuid', }); expect(localPlugin.device.launchProcess).toHaveBeenCalled(); }); it('disables its GActions when disconnected', function () { testRig.setConnected(false); expect(localPlugin.device.get_action_enabled('executeCommand')).toBeFalse(); expect(remotePlugin.device.get_action_enabled('executeCommand')).toBeFalse(); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/testSftpPlugin.js000066400000000000000000000061321421543444100311560ustar00rootroot00000000000000'use strict'; const Utils = imports.fixtures.utils; const {Plugin} = imports.service.plugin; const Packets = { response: { type: 'kdeconnect.sftp', body: { ip: '127.0.0.1', port: 2039, user: 'kdeconnect', password: 'remote-password', path: '/', multiPaths: [ '/remote-directory', ], pathNames: [ 'Remote', ], }, }, error: { type: 'kdeconnect.sftp', body: { errorMessage: 'Error Message', }, }, }; function handlePacket(packet) { switch (packet.type) { case 'kdeconnect.sftp.request': this.sendPacket(Packets.response); break; } } describe('The sftp plugin', function () { let testRig; let localPlugin; let remoteDevice; beforeAll(async function () { testRig = new Utils.TestRig(); await testRig.prepare({ localDevice: { incomingCapabilities: ['kdeconnect.sftp.request'], outgoingCapabilities: ['kdeconnect.sftp'], }, remoteDevice: { incomingCapabilities: ['kdeconnect.sftp'], outgoingCapabilities: ['kdeconnect.sftp'], }, }); testRig.setPaired(true); remoteDevice = testRig.remoteDevice; remoteDevice.handlePacket = handlePacket.bind(remoteDevice); }); afterAll(function () { testRig.destroy(); }); beforeEach(function () { if (localPlugin) { spyOn(localPlugin, 'handlePacket').and.callThrough(); spyOn(localPlugin, '_handleMount'); spyOn(localPlugin.device, 'showNotification'); } }); it('can be loaded', async function () { await testRig.loadPlugins(); localPlugin = testRig.localDevice._plugins.get('sftp'); expect(localPlugin).toBeDefined(); }); it('enables its GActions when connected', function () { testRig.setConnected(true); // NOTE: chaining-up to avoid the guard against Core.Channel type Plugin.prototype.connected.call(localPlugin); expect(localPlugin.device.get_action_enabled('mount')).toBeTrue(); expect(localPlugin.device.get_action_enabled('unmount')).toBeTrue(); }); it('can request a mount', async function () { localPlugin.mount(); await localPlugin.awaitPacket('kdeconnect.sftp', Packets.response.body); expect(localPlugin._handleMount).toHaveBeenCalled(); }); it('can handle error messages', async function () { remoteDevice.sendPacket(Packets.error); await localPlugin.awaitPacket('kdeconnect.sftp', Packets.error.body); expect(localPlugin.device.showNotification).toHaveBeenCalled(); }); it('disables its GActions when disconnected', function () { testRig.setConnected(false); expect(localPlugin.device.get_action_enabled('mount')).toBeFalse(); expect(localPlugin.device.get_action_enabled('unmount')).toBeFalse(); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/testSharePlugin.js000066400000000000000000000066411421543444100313110ustar00rootroot00000000000000'use strict'; const Utils = imports.fixtures.utils; describe('The share plugin', function () { let testRig; let localPlugin, remotePlugin; beforeAll(async function () { testRig = new Utils.TestRig(); await testRig.prepare({ localDevice: { incomingCapabilities: [ 'kdeconnect.share.request', ], outgoingCapabilities: [ 'kdeconnect.share.request', ], }, remoteDevice: { incomingCapabilities: [ 'kdeconnect.share.request', ], outgoingCapabilities: [ 'kdeconnect.share.request', ], }, }); testRig.setPaired(true); }); afterAll(function () { testRig.destroy(); }); beforeEach(function () { if (remotePlugin) spyOn(remotePlugin, 'handlePacket').and.callThrough(); }); it('can be loaded', async function () { await testRig.loadPlugins(); localPlugin = testRig.localDevice._plugins.get('share'); remotePlugin = testRig.remoteDevice._plugins.get('share'); expect(localPlugin).toBeDefined(); expect(remotePlugin).toBeDefined(); }); it('enables its GActions when connected', function () { testRig.setConnected(true); for (const action in localPlugin._meta.actions) expect(localPlugin.device.get_action_enabled(action)).toBeTrue(); for (const action in remotePlugin._meta.actions) expect(remotePlugin.device.get_action_enabled(action)).toBeTrue(); }); it('can send and receive files', async function () { spyOn(remotePlugin, '_handleFile'); localPlugin.shareFile(Utils.getDataPath('album.png')); await remotePlugin.awaitPacket('kdeconnect.share.request', { filename: 'album.png', }); expect(remotePlugin._handleFile).toHaveBeenCalled(); }); it('can send and receive text', async function () { spyOn(remotePlugin, '_handleText'); localPlugin.shareText('shared text'); await remotePlugin.awaitPacket('kdeconnect.share.request', { text: 'shared text', }); expect(remotePlugin._handleText).toHaveBeenCalled(); }); it('can send and receive URIs', async function () { spyOn(remotePlugin, '_handleUri'); localPlugin.shareUri('https://www.gnome.org/'); await remotePlugin.awaitPacket('kdeconnect.share.request', { url: 'https://www.gnome.org/', }); expect(remotePlugin._handleUri).toHaveBeenCalled(); }); xit('interprets file URIs as file shares', async function () { spyOn(remotePlugin, '_handleFile'); localPlugin.shareUri('file:///home/user/file.ext'); await remotePlugin.awaitPacket('kdeconnect.share.request', { filename: 'file.ext', }); expect(remotePlugin._handleFile).toHaveBeenCalled(); }); it('disables its GActions when disconnected', function () { testRig.setConnected(false); for (const action in localPlugin._meta.actions) expect(localPlugin.device.get_action_enabled(action)).toBeFalse(); for (const action in remotePlugin._meta.actions) expect(remotePlugin.device.get_action_enabled(action)).toBeFalse(); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/testSmsPlugin.js000066400000000000000000000204311421543444100310020ustar00rootroot00000000000000'use strict'; const Utils = imports.fixtures.utils; const Packets = { summary: { type: 'kdeconnect.sms.messages', body: { messages: [ { addresses: [ { address: '555-555-5555', }, ], body: 'incoming message of thread 1', date: 1588334621800, type: 1, read: 0, thread_id: 1, _id: 1, sub_id: 1, event: 1, }, { addresses: [ { address: '555-555-5556', }, ], body: 'incoming message of thread 2', date: 1588334621500, type: 1, read: 0, thread_id: 2, _id: 3, sub_id: 1, event: 1, }, ], version: 2, }, }, thread_one: { type: 'kdeconnect.sms.messages', body: { messages: [ { addresses: [ { address: '555-555-5555', }, ], body: 'incoming message of thread 1', date: 1588334621800, type: 1, read: 0, thread_id: 1, _id: 1, sub_id: 1, event: 1, }, { addresses: [ { address: '555-555-5555', }, ], body: 'outgoing message of thread 1', date: 1588334621700, type: 2, read: 0, thread_id: 1, _id: 2, sub_id: 1, event: 1, }, ], version: 2, }, }, thread_two: { type: 'kdeconnect.sms.messages', body: { messages: [ { addresses: [ { address: '555-555-5556', }, ], body: 'incoming message of thread 2', date: 1588334621500, type: 1, read: 0, thread_id: 2, _id: 3, sub_id: 1, event: 1, }, { addresses: [ { address: '555-555-5556', }, ], body: 'outgoing message of thread 2', date: 1588334621400, type: 2, read: 0, thread_id: 2, _id: 4, sub_id: 1, event: 1, }, ], version: 2, }, }, }; function handlePacket(packet) { switch (packet.type) { case 'kdeconnect.sms.request_conversations': this.sendPacket(Packets.summary); break; case 'kdeconnect.sms.request_conversation': if (packet.body.threadID === '1') this.sendPacket(Packets.thread_one); else if (packet.body.threadID === '2') this.sendPacket(Packets.thread_two); break; } } describe('The sms plugin', function () { let testRig; let localPlugin; let remoteDevice; beforeAll(async function () { testRig = new Utils.TestRig(); await testRig.prepare({ localDevice: { incomingCapabilities: [ 'kdeconnect.sms.messages', 'kdeconnect.sms.request', 'kdeconnect.sms.request_conversation', 'kdeconnect.sms.request_conversations', ], outgoingCapabilities: [ 'kdeconnect.sms.messages', 'kdeconnect.sms.request', 'kdeconnect.sms.request_conversation', 'kdeconnect.sms.request_conversations', ], }, remoteDevice: { incomingCapabilities: [ 'kdeconnect.sms.messages', 'kdeconnect.sms.request', 'kdeconnect.sms.request_conversation', 'kdeconnect.sms.request_conversations', ], outgoingCapabilities: [ 'kdeconnect.sms.messages', 'kdeconnect.sms.request', 'kdeconnect.sms.request_conversation', 'kdeconnect.sms.request_conversations', ], }, }); testRig.setPaired(true); remoteDevice = testRig.remoteDevice; remoteDevice.handlePacket = handlePacket.bind(remoteDevice); }); afterAll(function () { testRig.destroy(); }); beforeEach(function () { if (localPlugin) spyOn(localPlugin, 'handlePacket').and.callThrough(); }); it('can be loaded', async function () { await testRig.loadPlugins(); localPlugin = testRig.localDevice._plugins.get('sms'); expect(localPlugin).toBeDefined(); }); it('enables its GActions when connected', function () { spyOn(localPlugin, '_requestConversations'); testRig.setConnected(true); for (const action in localPlugin._meta.actions) expect(localPlugin.device.get_action_enabled(action)).toBeTrue(); }); it('requests messages when connected', function () { spyOn(localPlugin, '_requestConversations'); localPlugin.connected(); expect(localPlugin._requestConversations).toHaveBeenCalled(); }); it('can request a list of conversations', async function () { spyOn(localPlugin, '_handleDigest'); localPlugin._requestConversations(); await localPlugin.awaitPacket('kdeconnect.sms.messages'); expect(localPlugin._handleDigest).toHaveBeenCalled(); }); it('can request full conversations', async function () { spyOn(localPlugin, '_handleDigest').and.callThrough(); spyOn(localPlugin, '_handleThread').and.callThrough(); spyOn(localPlugin, '_requestConversation').and.callThrough(); localPlugin._requestConversations(); await localPlugin.awaitPacket('kdeconnect.sms.messages'); expect(localPlugin._handleDigest).toHaveBeenCalled(); expect(localPlugin._requestConversation).toHaveBeenCalledTimes(2); localPlugin.handlePacket.calls.reset(); await localPlugin.awaitPacket('kdeconnect.sms.messages'); expect(localPlugin._handleThread).toHaveBeenCalled(); }); it('only requests new or updated converations', async function () { spyOn(localPlugin, '_handleDigest').and.callThrough(); spyOn(localPlugin, '_handleThread').and.callThrough(); spyOn(localPlugin, '_requestConversation').and.callThrough(); localPlugin._requestConversations(); await localPlugin.awaitPacket('kdeconnect.sms.messages'); expect(localPlugin._handleDigest).toHaveBeenCalled(); expect(localPlugin._requestConversation).not.toHaveBeenCalled(); }); it('can send SMS messages', async function () { spyOn(remoteDevice, 'handlePacket').and.callThrough(); localPlugin.sendSms('555-555-5555', 'message body'); await remoteDevice.awaitPacket('kdeconnect.sms.request', { sendSms: true, phoneNumber: '555-555-5555', messageBody: 'message body', }); }); it('disables its GActions when disconnected', function () { testRig.setConnected(false); for (const action in localPlugin._meta.actions) expect(localPlugin.device.get_action_enabled(action)).toBeFalse(); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/testSystemvolumePlugin.js000066400000000000000000000065361421543444100327660ustar00rootroot00000000000000'use strict'; const Utils = imports.fixtures.utils; function handlePacket(packet) { switch (packet.type) { case 'kdeconnect.systemvolume': break; case 'kdeconnect.systemvolume.request': break; } } describe('The systemvolume plugin', function () { let testRig; let localPlugin; let remoteDevice; beforeAll(async function () { Utils.mockComponents(); testRig = new Utils.TestRig(); await testRig.prepare({ localDevice: { incomingCapabilities: [ 'kdeconnect.systemvolume', 'kdeconnect.systemvolume.request', ], outgoingCapabilities: [ 'kdeconnect.systemvolume', 'kdeconnect.systemvolume.request', ], }, remoteDevice: { incomingCapabilities: [ 'kdeconnect.systemvolume', 'kdeconnect.systemvolume.request', ], outgoingCapabilities: [ 'kdeconnect.systemvolume', 'kdeconnect.systemvolume.request', ], }, }); testRig.setPaired(true); remoteDevice = testRig.remoteDevice; remoteDevice.handlePacket = handlePacket.bind(remoteDevice); }); afterAll(function () { testRig.destroy(); }); beforeEach(function () { if (localPlugin) spyOn(localPlugin, 'handlePacket').and.callThrough(); }); it('can be loaded', async function () { await testRig.loadPlugins(); localPlugin = testRig.localDevice._plugins.get('systemvolume'); expect(localPlugin).toBeDefined(); }); it('sends streams when connected', function () { spyOn(localPlugin, '_sendSinkList'); testRig.setConnected(true); expect(localPlugin._sendSinkList).toHaveBeenCalled(); }); it('sends a list of streams when requested', async function () { spyOn(remoteDevice, 'handlePacket').and.callThrough(); remoteDevice.sendPacket({ type: 'kdeconnect.systemvolume.request', body: { requestSinks: true, }, }); await localPlugin.awaitPacket('kdeconnect.systemvolume.request', { requestSinks: true, }); await remoteDevice.awaitPacket('kdeconnect.systemvolume'); }); it('handles volume level requests', async function () { remoteDevice.sendPacket({ type: 'kdeconnect.systemvolume.request', body: { name: '0', volume: 2, }, }); await localPlugin.awaitPacket('kdeconnect.systemvolume.request', { name: '0', volume: 2, }); expect(localPlugin._mixer.lookup_sink(0).volume).toBe(2); }); it('handles mute requests', async function () { remoteDevice.sendPacket({ type: 'kdeconnect.systemvolume.request', body: { name: '0', muted: true, }, }); await localPlugin.awaitPacket('kdeconnect.systemvolume.request', { name: '0', muted: true, }); expect(localPlugin._mixer.lookup_sink(0).muted).toBeTrue(); }); }); gnome-shell-extension-gsconnect-50/installed-tests/suites/plugins/testTelephonyPlugin.js000066400000000000000000000236071421543444100322170ustar00rootroot00000000000000'use strict'; const Utils = imports.fixtures.utils; const Packets = { ringing: { type: 'kdeconnect.telephony', body: { contactName: 'Name', phoneNumber: '555-555-5555', event: 'ringing', }, }, ringingCancel: { type: 'kdeconnect.telephony', body: { isCancel: true, contactName: 'Name', phoneNumber: '555-555-5555', event: 'ringing', }, }, talking: { type: 'kdeconnect.telephony', body: { contactName: 'Name', phoneNumber: '555-555-5555', event: 'talking', }, }, talkingCancel: { type: 'kdeconnect.telephony', body: { isCancel: true, contactName: 'Name', phoneNumber: '555-555-5555', event: 'ringing', }, }, }; describe('The telephony plugin', function () { let testRig; let localPlugin, remotePlugin; beforeAll(async function () { Utils.mockComponents(); testRig = new Utils.TestRig(); await testRig.prepare({ localDevice: { incomingCapabilities: [ 'kdeconnect.telephony.request', 'kdeconnect.telephony.request_mute', ], outgoingCapabilities: [ 'kdeconnect.telephony', ], }, remoteDevice: { incomingCapabilities: [ 'kdeconnect.telephony.request', 'kdeconnect.telephony.request_mute', ], outgoingCapabilities: [ 'kdeconnect.telephony', ], }, }); testRig.setPaired(true); testRig.setConnected(true); }); afterAll(function () { testRig.destroy(); }); beforeEach(function () { if (localPlugin) { spyOn(localPlugin, 'handlePacket').and.callThrough(); spyOn(localPlugin.device, 'showNotification'); spyOn(localPlugin.device, 'hideNotification'); } }); it('can be loaded', async function () { await testRig.loadPlugins(); localPlugin = testRig.localDevice._plugins.get('telephony'); remotePlugin = testRig.remoteDevice._plugins.get('telephony'); expect(localPlugin).toBeDefined(); expect(remotePlugin).toBeDefined(); // Unset the event triggers for initial tests localPlugin.settings.set_string('ringing-volume', 'nothing'); localPlugin.settings.set_boolean('ringing-pause', false); localPlugin.settings.set_string('talking-volume', 'nothing'); localPlugin.settings.set_boolean('talking-microphone', false); localPlugin.settings.set_boolean('talking-pause', false); }); it('enables its GActions when connected', function () { testRig.setConnected(true); expect(localPlugin.device.get_action_enabled('muteCall')).toBeTrue(); }); it('shows a notification when the phone is ringing', async function () { remotePlugin.device.sendPacket(Packets.ringing); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.ringing.body); expect(localPlugin.device.showNotification).toHaveBeenCalled(); }); it('hides the notification if the phone stops ringing', async function () { remotePlugin.device.sendPacket(Packets.ringingCancel); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.ringingCancel.body); expect(localPlugin.device.hideNotification).toHaveBeenCalled(); }); it('shows a notification when the phone is answered', async function () { remotePlugin.device.sendPacket(Packets.talking); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.talking.body); expect(localPlugin.device.showNotification).toHaveBeenCalled(); }); it('hides the notification when the call ends', async function () { remotePlugin.device.sendPacket(Packets.talkingCancel); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.talkingCancel.body); expect(localPlugin.device.hideNotification).toHaveBeenCalled(); }); describe('can lower and restore the volume', function () { let localMixer; beforeEach(function () { localMixer = localPlugin._mixer; spyOn(localMixer, 'lowerVolume'); spyOn(localMixer, 'restore'); }); it('when the phone is ringing', async function () { localPlugin.settings.set_string('ringing-volume', 'lower'); remotePlugin.device.sendPacket(Packets.ringing); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.ringing.body); expect(localMixer.lowerVolume).toHaveBeenCalled(); remotePlugin.device.sendPacket(Packets.ringingCancel); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.ringingCancel.body); expect(localMixer.restore).toHaveBeenCalled(); }); it('when the phone is answered', async function () { localPlugin.settings.set_string('talking-volume', 'lower'); // Start remotePlugin.device.sendPacket(Packets.talking); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.talking.body); expect(localMixer.lowerVolume).toHaveBeenCalled(); // End remotePlugin.device.sendPacket(Packets.talkingCancel); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.talkingCancel.body); expect(localMixer.restore).toHaveBeenCalled(); }); }); describe('can mute and unmute the volume', function () { let localMixer; beforeEach(function () { localMixer = localPlugin._mixer; spyOn(localMixer, 'muteVolume'); spyOn(localMixer, 'restore'); }); it('when the phone is ringing', async function () { localPlugin.settings.set_string('ringing-volume', 'mute'); remotePlugin.device.sendPacket(Packets.ringing); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.ringing.body); expect(localMixer.muteVolume).toHaveBeenCalled(); remotePlugin.device.sendPacket(Packets.ringingCancel); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.ringingCancel.body); expect(localMixer.restore).toHaveBeenCalled(); }); it('when the phone is answered', async function () { localPlugin.settings.set_string('talking-volume', 'mute'); // Start remotePlugin.device.sendPacket(Packets.talking); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.talking.body); expect(localMixer.muteVolume).toHaveBeenCalled(); // End remotePlugin.device.sendPacket(Packets.talkingCancel); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.talkingCancel.body); expect(localMixer.restore).toHaveBeenCalled(); }); }); describe('can mute and unmute the microphone', function () { let localMixer; beforeEach(function () { localMixer = localPlugin._mixer; spyOn(localMixer, 'muteMicrophone'); spyOn(localMixer, 'restore'); }); it('when the phone is answered', async function () { localPlugin.settings.set_boolean('talking-microphone', true); // Start remotePlugin.device.sendPacket(Packets.talking); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.talking.body); expect(localMixer.muteMicrophone).toHaveBeenCalled(); // End remotePlugin.device.sendPacket(Packets.talkingCancel); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.talkingCancel.body); expect(localMixer.restore).toHaveBeenCalled(); }); }); describe('can pause and unpause media', function () { let localMedia; beforeEach(function () { localMedia = localPlugin._mpris; spyOn(localMedia, 'pauseAll'); spyOn(localMedia, 'unpauseAll'); }); it('when the phone is ringing', async function () { localPlugin.settings.set_boolean('ringing-pause', true); remotePlugin.device.sendPacket(Packets.ringing); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.ringing.body); expect(localMedia.pauseAll).toHaveBeenCalled(); remotePlugin.device.sendPacket(Packets.ringingCancel); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.ringingCancel.body); expect(localMedia.unpauseAll).toHaveBeenCalled(); }); it('when the phone is answered', async function () { localPlugin.settings.set_boolean('talking-pause', true); // Start remotePlugin.device.sendPacket(Packets.talking); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.talking.body); expect(localMedia.pauseAll).toHaveBeenCalled(); // End remotePlugin.device.sendPacket(Packets.talkingCancel); await localPlugin.awaitPacket('kdeconnect.telephony', Packets.talkingCancel.body); expect(localMedia.unpauseAll).toHaveBeenCalled(); }); }); it('disabled its GActions when disconnected', function () { testRig.setConnected(false); expect(localPlugin.device.get_action_enabled('muteCall')).toBeFalse(); }); }); gnome-shell-extension-gsconnect-50/meson.build000066400000000000000000000062701421543444100216560ustar00rootroot00000000000000project('gsconnect', 'c', version: '50', meson_version: '>= 0.46.0' ) gnome = import('gnome') i18n = import('i18n') app_id = 'org.gnome.Shell.Extensions.GSConnect' app_path = '/org/gnome/Shell/Extensions/GSConnect' extuuid = 'gsconnect@andyholmes.github.io' prefix = get_option('prefix') datadir = join_paths(prefix, get_option('datadir')) extdatadir = join_paths(datadir, 'gnome-shell', 'extensions', extuuid) libdir = join_paths(prefix, get_option('libdir')) libexecdir = join_paths(prefix, get_option('libexecdir')) localedir = join_paths(prefix, get_option('localedir')) sysconfdir = get_option('sysconfdir') # GSettings schema dir if get_option('gsettings_schemadir') != '' gschemadir = get_option('gsettings_schemadir') else gschemadir = join_paths(datadir, 'glib-2.0', 'schemas') endif # GNOME Shell LIBDIR if get_option('gnome_shell_libdir') != '' gnome_shell_libdir = get_option('gnome_shell_libdir') else gnome_shell_libdir = libdir endif # Configuration extconfig = configuration_data() extconfig.set('PACKAGE_VERSION', meson.project_version()) extconfig.set('PACKAGE_URL', 'https://github.com/GSConnect/gnome-shell-extension-gsconnect') extconfig.set('PACKAGE_BUGREPORT', 'https://github.com/GSConnect/gnome-shell-extension-gsconnect/issues/new') extconfig.set('PACKAGE_DATADIR', extdatadir) extconfig.set('PACKAGE_LOCALEDIR', localedir) extconfig.set('GSETTINGS_SCHEMA_DIR', gschemadir) extconfig.set('APPLICATION_ID', app_id) extconfig.set('APPLICATION_PATH', app_path) extconfig.set('GNOME_SHELL_LIBDIR', gnome_shell_libdir) extconfig.set('FFMPEG_PATH', get_option('ffmpeg_path')) extconfig.set('OPENSSL_PATH', get_option('openssl_path')) extconfig.set('SSHADD_PATH', get_option('sshadd_path')) extconfig.set('SSHKEYGEN_PATH', get_option('sshkeygen_path')) # ZIP targets for user extension builds env_util = find_program('env') run_target( 'make-zip', command: [ env_util, 'UUID=' + extuuid, 'DATADIR=' + datadir, 'LOCALEDIR=' + localedir, 'GSCHEMADIR=' + gschemadir, join_paths(meson.source_root(), 'build-aux', 'ego', 'mkzip.sh') ] ) run_target( 'install-zip', command: [ env_util, 'UUID=' + extuuid, 'DATADIR=' + datadir, 'LOCALEDIR=' + localedir, 'GSCHEMADIR=' + gschemadir, 'INSTALL=true', join_paths(meson.source_root(), 'build-aux', 'ego', 'mkzip.sh') ] ) # Post-Install script for distributions without the hooks if get_option('post_install') meson.add_install_script( env_util.path(), 'GSCHEMADIR=' + gschemadir, join_paths(meson.source_root(), 'build-aux', 'meson', 'post-install.sh') ) endif # Extension Source install_subdir( 'src', install_dir: extdatadir, strip_directory: true ) eslint = find_program('eslint', required: false) if eslint.found() test('ESLint (Source)', eslint, args: join_paths(meson.source_root(), 'src'), suite: 'lint', ) test('ESLint (Installed Tests)', eslint, args: join_paths(meson.source_root(), 'installed-tests'), suite: 'lint', ) test('ESLint (WebExtension)', eslint, args: join_paths(meson.source_root(), 'webextension'), suite: 'lint', ) endif subdir('data') subdir('installed-tests') subdir('nautilus-extension') subdir('po') gnome-shell-extension-gsconnect-50/meson_options.txt000066400000000000000000000046651421543444100231570ustar00rootroot00000000000000# See https://github.com/GSConnect/gnome-shell-extension-gsconnect/wiki/Packaging # Run meson/post-install.sh (glib-compile-schemas) option( 'post_install', type: 'boolean', value: false, description: 'Run meson/post-install.sh (eg. glib-compile-schemas)' ) # GNOME Shell LIBDIR option( 'gnome_shell_libdir', type: 'string', value: '', description: 'LIBDIR for GNOME Shell (eg. $LIBDIR/gnome-shell/Gvc-1.0.typelib)' ) # GSettings schema directory option( 'gsettings_schemadir', type: 'string', value: '', description: 'GSettings Schema directory' ) # DBus service file option( 'session_bus_services_dir', type: 'string', value: '', description: 'DBus session services directory' ) # firewalld service file option( 'firewalld', type: 'boolean', value: false, description: 'Install firewalld service file' ) # External program paths # NOTE: these are only useful for distributions like NixOS that don't use PATH option( 'ffmpeg_path', type: 'string', value: 'ffmpeg', description: 'Absolute path to ffmpeg binary' ) option( 'openssl_path', type: 'string', value: 'openssl', description: 'Absolute path to openssl binary' ) option( 'sshadd_path', type: 'string', value: 'ssh-add', description: 'Absolute path to ssh-add binary' ) option( 'sshkeygen_path', type: 'string', value: 'ssh-keygen', description: 'Absolute path to ssh-keygen binary' ) # Nautilus/Nemo Python extension installation option( 'nautilus', type: 'boolean', value: true, description: 'Install file browser extension for Nautilus (Files)' ) option( 'nemo', type: 'boolean', value: false, description: 'Install file browser extension for Nemo' ) # WebExtension manifest installation # NOTE: this is NOT the WebExtension, but is REQUIRED for it to work. option( 'webextension', type: 'boolean', value: true, description: 'Install WebExtension manifest for Chrome, Chromium & Firefox' ) # Override manifest install so that BROWSER_NMHDIR/foo.json option( 'chrome_nmhdir', type: 'string', value: '', description: 'Native Messaging Host directory for Chrome' ) option( 'chromium_nmhdir', type: 'string', value: '', description: 'Native Messaging Host directory for Chromium' ) option( 'mozilla_nmhdir', type: 'string', value: '', description: 'Native Messaging Host directory for Mozilla' ) option( 'installed_tests', type: 'boolean', value: true, description: 'Install tests' ) gnome-shell-extension-gsconnect-50/nautilus-extension/000077500000000000000000000000001421543444100233655ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/nautilus-extension/meson.build000066400000000000000000000005361421543444100255330ustar00rootroot00000000000000# Nautilus Extension if get_option('nautilus') install_data( 'nautilus-gsconnect.py', install_dir: join_paths(datadir, 'nautilus-python', 'extensions') ) endif # Nemo Extension if get_option('nemo') install_data( 'nautilus-gsconnect.py', rename: join_paths(datadir, 'nemo-python', 'extensions', 'nemo-gsconnect.py') ) endif gnome-shell-extension-gsconnect-50/nautilus-extension/nautilus-gsconnect.py000066400000000000000000000142371421543444100275730ustar00rootroot00000000000000""" nautilus-gsconnect.py - A Nautilus extension for sending files via GSConnect. A great deal of credit and appreciation is owed to the indicator-kdeconnect developers for the sister Python script 'kdeconnect-send-nautilus.py': https://github.com/Bajoja/indicator-kdeconnect/blob/master/data/extensions/kdeconnect-send-nautilus.py """ import gettext import os.path import sys import gi gi.require_version('Gio', '2.0') gi.require_version('GLib', '2.0') gi.require_version('GObject', '2.0') from gi.repository import Gio, GLib, GObject # Host application detection # # Nemo seems to reliably identify itself as 'nemo' in argv[0], so we # can test for that. Nautilus detection is less reliable, so don't try. # See https://github.com/linuxmint/nemo-extensions/issues/330 if "nemo" in sys.argv[0].lower(): # Host runtime is nemo-python gi.require_version('Nemo', '3.0') from gi.repository import Nemo as FileManager else: # Otherwise, just assume it's nautilus-python gi.require_version('Nautilus', '3.0') from gi.repository import Nautilus as FileManager SERVICE_NAME = 'org.gnome.Shell.Extensions.GSConnect' SERVICE_PATH = '/org/gnome/Shell/Extensions/GSConnect' # Init gettext translations LOCALE_DIR = os.path.join(GLib.get_user_data_dir(), 'gnome-shell', 'extensions', 'gsconnect@andyholmes.github.io', 'locale') if not os.path.exists(LOCALE_DIR): LOCALE_DIR = None try: i18n = gettext.translation(SERVICE_NAME, localedir=LOCALE_DIR) _ = i18n.gettext except (IOError, OSError) as e: print('GSConnect: {0}'.format(e.strerror)) i18n = gettext.translation(SERVICE_NAME, localedir=LOCALE_DIR, fallback=True) _ = i18n.gettext class GSConnectShareExtension(GObject.Object, FileManager.MenuProvider): """A context menu for sending files via GSConnect.""" def __init__(self): """Initialize the DBus ObjectManager""" GObject.Object.__init__(self) self.devices = {} Gio.DBusProxy.new_for_bus(Gio.BusType.SESSION, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, SERVICE_NAME, SERVICE_PATH, 'org.freedesktop.DBus.ObjectManager', None, self._init_async, None) def _init_async(self, proxy, res, user_data): proxy = proxy.new_for_bus_finish(res) proxy.connect('notify::g-name-owner', self._on_name_owner_changed) proxy.connect('g-signal', self._on_g_signal) self._on_name_owner_changed(proxy, None) def _on_g_signal(self, proxy, sender_name, signal_name, parameters): # Wait until the service is ready if proxy.props.g_name_owner is None: return objects = parameters.unpack() if signal_name == 'InterfacesAdded': for object_path, props in objects.items(): props = props['org.gnome.Shell.Extensions.GSConnect.Device'] self.devices[object_path] = (props['Name'], Gio.DBusActionGroup.get( proxy.get_connection(), SERVICE_NAME, object_path)) elif signal_name == 'InterfacesRemoved': for object_path in objects: try: del self.devices[object_path] except KeyError: pass def _on_name_owner_changed(self, proxy, pspec): # Wait until the service is ready if proxy.props.g_name_owner is None: self.devices = {} else: proxy.call('GetManagedObjects', None, Gio.DBusCallFlags.NO_AUTO_START, -1, None, self._get_managed_objects, None) def _get_managed_objects(self, proxy, res, user_data): objects = proxy.call_finish(res)[0] for object_path, props in objects.items(): props = props['org.gnome.Shell.Extensions.GSConnect.Device'] if not props: continue self.devices[object_path] = (props['Name'], Gio.DBusActionGroup.get( proxy.get_connection(), SERVICE_NAME, object_path)) def send_files(self, menu, files, action_group): """Send *files* to *device_id*""" for file in files: variant = GLib.Variant('(sb)', (file.get_uri(), False)) action_group.activate_action('shareFile', variant) def get_file_items(self, window, files): """Return a list of select files to be sent""" # Only accept regular files for uri in files: if uri.get_uri_scheme() != 'file' or uri.is_directory(): return () # Enumerate capable devices devices = [] for name, action_group in self.devices.values(): if action_group.get_action_enabled('shareFile'): devices.append([name, action_group]) # No capable devices; don't show menu entry if not devices: return () # Context Menu Item menu = FileManager.MenuItem( name='GSConnectShareExtension::Devices', label=_('Send To Mobile Device') ) # Context Submenu submenu = FileManager.Menu() menu.set_submenu(submenu) # Context Submenu Items for name, action_group in devices: item = FileManager.MenuItem( name='GSConnectShareExtension::Device' + name, label=name ) item.connect('activate', self.send_files, files, action_group) submenu.append_item(item) return (menu,) gnome-shell-extension-gsconnect-50/po/000077500000000000000000000000001421543444100201255ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/po/LINGUAS000066400000000000000000000001511421543444100211470ustar00rootroot00000000000000ar be ca cs da de es et fa fr fi gl hu it lt nl_BE nl_NL pl pt_BR ru sk sr sr@latin sv tr uk zh_CN zh_TW gnome-shell-extension-gsconnect-50/po/POTFILES000066400000000000000000000031021421543444100212710ustar00rootroot00000000000000data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in data/ui/connect-dialog.ui data/ui/contact-chooser.ui data/ui/contacts-address-row.ui data/ui/legacy-messaging-dialog.ui data/ui/messaging-conversation-message.ui data/ui/messaging-conversation-summary.ui data/ui/messaging-conversation.ui data/ui/messaging-window.ui data/ui/mousepad-input-dialog.ui data/ui/notification-reply-dialog.ui data/ui/preferences-command-editor.ui data/ui/preferences-device-panel.ui data/ui/preferences-section-row.ui data/ui/preferences-shortcut-editor.ui data/ui/preferences-window.ui data/ui/service-device-chooser.ui data/ui/service-error-dialog.ui nautilus-extension/nautilus-gsconnect.py src/extension.js src/preferences/device.js src/preferences/keybindings.js src/preferences/service.js src/service/daemon.js src/service/device.js src/service/manager.js src/service/backends/lan.js src/service/plugins/battery.js src/service/plugins/clipboard.js src/service/plugins/contacts.js src/service/plugins/findmyphone.js src/service/plugins/mousepad.js src/service/plugins/mpris.js src/service/plugins/notification.js src/service/plugins/photo.js src/service/plugins/ping.js src/service/plugins/presenter.js src/service/plugins/runcommand.js src/service/plugins/sftp.js src/service/plugins/share.js src/service/plugins/sms.js src/service/plugins/systemvolume.js src/service/plugins/telephony.js src/service/ui/contacts.js src/service/ui/legacyMessaging.js src/service/ui/messaging.js src/service/ui/mousepad.js src/service/ui/service.js src/shell/device.js src/shell/notification.js webextension/gettext.js gnome-shell-extension-gsconnect-50/po/ar.po000066400000000000000000000751521421543444100211010ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-09 01:51\n" "Last-Translator: \n" "Language-Team: Arabic\n" "Language: ar_SA\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: ar\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "الاتصال بـ…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "؄لغاؔ" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Ų§ŲŖŲµŲ§Ł„" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "Ų¹Ł†ŁˆŲ§Ł† الـ IP" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "لا توجد جهات Ų§ŲŖŲµŲ§Ł„" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "المساعدة" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "اكتب رقم هاتف أو اسم" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Ų„Ų±Ų³Ų§Ł„ رسالة" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "اكتب رسالة" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "المراسة" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Ł…Ų­Ų§ŲÆŲ«Ų© جديدة" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "لا توجد Ł…Ų­Ų§ŲÆŲ«Ų§ŲŖ" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "لم ŁŠŲŖŁ… تحديد أي Ł…Ų­Ų§ŲÆŲ«Ų©" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "حفظ" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "الاسم" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Ų³Ų·Ų± Ų§Ł„Ų£ŁˆŲ§Ł…Ų±" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Ų§Ų®ŲŖŲ± ملف ŲŖŁ†ŁŁŠŲ°ŁŠ" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Ų³Ų·Ų­ Ų§Ł„Ł…ŁƒŲŖŲØ" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "Ų§Ł„ŁƒŲ§Ł…ŁŠŲ±Ų§" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "مزامنة الحافظة" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "مؓغلات Ų§Ł„ŁˆŲ³Ų§Ų¦Ų·" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "الفأرة ŁˆŁ„ŁˆŲ­Ų© Ų§Ł„Ł…ŁŲ§ŲŖŁŠŲ­" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Ų§Ł„ŲŖŲ­ŁƒŁ… ŲØŁ…Ų³ŲŖŁˆŁ‰ Ų§Ł„ŲµŁˆŲŖ" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "الملفات" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "استلام ملفات" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Ų§Ł„Ł…Ų“Ų§Ų±ŁƒŲ©" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "بطارية الجهاز" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Ų„Ų“Ų¹Ų§Ų± انخفاض Ų§Ł„ŲØŲ·Ų§Ų±ŁŠŲ©" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Ų„Ų“Ų¹Ų§Ų± امتلاؔ Ų§Ł„ŲØŲ·Ų§Ų±ŁŠŲ©" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "بطارية الجهاز" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Ł…Ų“Ų§Ų±ŁƒŲ© Ų§Ł„Ų§Ų­ŲµŲ§Ų¦ŁŠŲ§ŲŖ" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Ų§Ł„ŲØŲ·Ų§Ų±ŁŠŲ©" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Ų§ļ»·ŁˆŲ§Ł…Ų±" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Ł…Ų“Ų§Ų±ŁƒŲ© ال؄ؓعارات" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Ų§Ł„ŲŖŲ·ŲØŁŠŁ‚Ų§ŲŖ" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "ال؄ؓعارات" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "جهات الاتصال" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Ų§Ł„Ł…ŁƒŲ§Ł„Ł…Ų§ŲŖ Ų§Ł„ŁˆŲ§Ų±ŲÆŲ©" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Ł…Ų³ŲŖŁˆŁ‰ Ų§Ł„ŲµŁˆŲŖ" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Ų„ŁŠŁ‚Ų§Ł Ų§Ł„ŁˆŲ³Ų§Ų¦Ų·" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Ų§Ł„Ł…ŁƒŲ§Ł„Ł…Ų© Ų§Ł„Ų­Ų§Ł„ŁŠŲ©" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "ŁƒŲŖŁ… Ų§Ł„Ł…Ų§ŁŠŁƒŲ±ŁˆŁŁˆŁ†" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "المهاتفة" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Ų§Ų®ŲŖŲµŲ§Ų±Ų§ŲŖ اﻹجراؔات" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Ų„Ų¹Ų§ŲÆŲ© ŲŖŲ¹ŁŠŁŠŁ† Ų§Ł„ŁƒŁ„ā€¦" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "الاختصارات" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "ال؄ضافات" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "تجريبي" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "دعم الرسائل Ų§Ł„Ł†ŲµŁŠŲ© Ų§Ł„Ł‚ŲÆŁŠŁ…" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "خيارات متقدمة" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Ų§Ų®ŲŖŲµŲ§Ų±Ų§ŲŖ Ł„ŁˆŲ­Ų© Ų§Ł„Ł…ŁŲ§ŲŖŁŠŲ­" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "اقتران" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "الجهاز غير مقترن" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "قد ŲŖŲ­ŲŖŲ§Ų¬ ؄لى Ų„Ų¹ŲÆŲ§ŲÆ هذا الجهاز Ų£ŁˆŁ„Ų§ قبل اﻹقتران" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Ł…Ų¹Ł„ŁˆŁ…Ų§ŲŖ Ų§Ł„ŲŖŲ“ŁŁŠŲ±" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "؄لغاؔ الاقتران" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "؄لى الجهاز" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "من الجهاز" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "لا ؓيؔ" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "منخفض" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "ŁƒŲŖŁ…" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "" #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "لم ŁŠŲŖŁ… Ų§Ł„Ų¹Ų«ŁˆŲ± على أي جهاز" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "" #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "" msgstr[1] "" msgstr[2] "" msgstr[3] "" msgstr[4] "" msgstr[5] "" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "ŲŖŲ¹ŲÆŁŠŁ„" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "؄زالة" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "" #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "" #: src/preferences/service.js:414 msgid "Review Log" msgstr "" #: src/preferences/service.js:482 msgid "Laptop" msgstr "" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "" #: src/preferences/service.js:486 msgid "Tablet" msgstr "" #: src/preferences/service.js:488 msgid "Television" msgstr "" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "" #: src/preferences/service.js:518 msgid "Connected" msgstr "" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "" #: src/service/daemon.js:351 msgid "List available devices" msgstr "" #: src/service/daemon.js:360 msgid "List all devices" msgstr "" #: src/service/daemon.js:369 msgid "Target Device" msgstr "" #: src/service/daemon.js:411 msgid "Message Body" msgstr "" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "" #: src/service/daemon.js:528 msgid "Show release version" msgstr "" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "" #: src/service/device.js:800 msgid "Reject" msgstr "" #: src/service/device.js:805 msgid "Accept" msgstr "" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "" #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "" msgstr[1] "" msgstr[2] "" msgstr[3] "" msgstr[4] "" msgstr[5] "" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "" msgstr[1] "" msgstr[2] "" msgstr[3] "" msgstr[4] "" msgstr[5] "" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "" #: src/shell/notification.js:54 msgid "Reply" msgstr "" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "" #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "الخدمة غير Ł…ŲŖŲ§Ų­Ų©" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "فتح في المتصفح" gnome-shell-extension-gsconnect-50/po/be.po000066400000000000000000001163341421543444100210630ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2022-02-14 23:26\n" "Last-Translator: \n" "Language-Team: Belarusian\n" "Language: be_BY\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || n%10>=5 && n%10<=9 || n%100>=11 && n%100<=14 ? 2 : 3);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: be\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "Š ŃŠ°Š»Ń–Š·Š°Ń†Ń‹Ń KDE Connect Š“Š»Ń GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "КаманГа GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnect — Š³ŃŃ‚Š° Š³Š°Ń‚Š¾Š²Š°Ń Ń€ŃŠ°Š»Ń–Š·Š°Ń†Ń‹Ń KDE Connect, ŃŠæŠµŃ†Ń‹ŃŠ»ŃŒŠ½Š° Š“Š»Ń GNOME Shell Š· Ń–Š½Ń‚ŃŠ³Ń€Š°Ń†Ń‹ŃŠ¹ у Nautilus, Chrome і Firefox. КаманГа KDE Connect мае праграмы Š“Š»Ń Linux, BSD, Android, Sailfish, macOS і Windows." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "Š— Гапамогай GSConnect вы можаце Š±ŃŃŠæŠµŃ‡Š½Š° ŠæŠ°Š“ŠŗŠ»ŃŽŃ‡Š°Ń†Ń†Š° Га Š¼Š°Š±Ń–Š»ŃŒŠ½Ń‹Ń… і Ń–Š½ŃˆŃ‹Ń… ŃŃ‚Š°Ń†Ń‹ŃŠ½Š°Ń€Š½Ń‹Ń… прылаГы, каб:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "ŠŠ±Š°Š³ŃƒŠ»ŃŒŠ²Š°Ń†ŃŒ файлы, спасылкі і Ń‚ŃŠŗŃŃ‚" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "ŠŠ“ŠæŃ€Š°ŃžŠ»ŃŃ†ŃŒ і ŠæŃ€Ń‹Š¼Š°Ń†ŃŒ павеГамленні" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Š”Ń–Š½Ń…Ń€Š°Š½Ń–Š·Š°Š²Š°Ń†ŃŒ змесціва Š±ŃƒŃ„ера абмену" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Š”Ń–Š½Ń…Ń€Š°Š½Ń–Š·Š°Š²Š°Ń†ŃŒ кантакты" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Š”Ń–Š½Ń…Ń€Š°Š½Ń–Š·Š°Š²Š°Ń†ŃŒ Š°ŠæŠ°Š²ŃŃˆŃ‡ŃŠ½Š½Ń–" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "ŠšŃ–Ń€Š°Š²Š°Ń†ŃŒ Š¼ŠµŠ“Ń‹ŃŠæŃ€Š°Š¹Š³Ń€Š°Š²Š°Š»ŃŒŠ½Ń–ŠŗŠ°Š¼Ń–" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "ŠšŃ–Ń€Š°Š²Š°Ń†ŃŒ Š³ŃƒŃ‡Š½Š°ŃŃ†ŃŽ ŃŃ–ŃŃ‚ŃŠ¼Ń‹" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Š’Ń‹ŠŗŠ¾Š½Š²Š°Ń†ŃŒ ŠæŠ°ŠæŃŃ€ŃŠ“Š½Šµ Š·Š°Š“Š°Š“Š·ŠµŠ½Ń‹Ń каманГы" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "І Ń–Š½ŃˆŠ°Šµā€¦" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect у GNOME Shell" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "ŠŸŠ°Š“Š»ŃƒŃ‡Ń‹Ń†Ń†Š° Га…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Š”ŠŗŠ°ŃŠ°Š²Š°Ń†ŃŒ" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Š—Š»ŃƒŃ‡ŃŠ½Š½Šµ" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP-аГрас" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "ŠŃŠ¼Š° ŠŗŠ°Š½Ń‚Š°ŠŗŃ‚Š°Ńž" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "ДавеГка" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Увесці Š½ŃƒŠ¼Š°Ń€ Ń‚ŃŠ»ŠµŃ„Š¾Š½Š° або Ń–Š¼Ń" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Š†Š½ŃˆŠ°Šµ" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "ŠŠ“ŠæŃ€Š°Š²Ń–Ń†ŃŒ SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "ŠŠ“ŠæŃ€Š°Š²Ń–Ń†ŃŒ" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "ŠŸŃ€Ń‹Š»Š°Š“Š° Š°Š“Š»ŃƒŃ‡Š°Š½Š°" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "ŠŠ“ŠæŃ€Š°Š²Ń–Ń†ŃŒ павеГамленне" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "ŠŠ°ŠæŃ–ŃŠ°Ń†ŃŒ павеГамлене" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "Запіс ŠæŠ°Š²ŠµŠ“Š°Š¼Š»ŠµŠ½Š½Ń" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Š£Š²ŃŠ“Š·Ń–Ń†Šµ павеГамленне націсніце Enter Š“Š»Ń Š°Š“ŠæŃ€Š°ŃžŠŗŃ–" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "ŠŸŠ°Š²ŠµŠ“Š°Š¼Š»ŠµŠ½Š½Ń–" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "ŠŠ¾Š²Š°Ń размова" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "ŠŃŠ¼Š° Ń€Š°Š·Š¼Š¾Ńž" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "ŠŠµ выбрана размова" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Выберыце або пачніце Ń€Š°Š·Š¼Š¾Š²Ńƒ" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Š ŃŠ“Š°Š³Š°Š²Š°Ń†ŃŒ каманГу" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Š—Š°Ń…Š°Š²Š°Ń†ŃŒ" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "ŠŠ°Š·Š²Š°" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "ŠšŠ°Š¼Š°Š½Š“Š½Ń‹ раГок" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Š’Ń‹Š±Ń€Š°Ń†ŃŒ Š²Ń‹ŠŗŠ¾Š½Š²Š°Š»ŃŒŠ½Ń‹ файл" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "ŠŠ“ŠŗŃ€Ń‹Ń†ŃŒ" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Камп'ŃŽŃ‚Š°Ń€" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "ŠšŠ°Š¼ŠµŃ€Š°" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Š”Ń–Š½Ń…Ń€Š°Š½Ń–Š·Š°Ń†Ń‹Ń Š±ŃƒŃ„ŠµŃ€Ńƒ абмену" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "ŠœŠµŠ“Ń‹ŃŠæŃ€Š°Š¹Š³Ń€Š°Š²Š°Š»ŃŒŠ½Ń–ŠŗŃ–" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "ŠœŃ‹Ńˆ і ŠŗŠ»Š°Š²Ń–ŃŃ‚ŃƒŃ€Š°" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "ŠšŃ–Ń€Š°Š²Š°Š½Š½Šµ Š³ŃƒŃ‡Š½Š°ŃŃ†ŃŽ" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Файлы" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "ŠŃ‚Ń€Ń‹Š¼Š»Ń–Š²Š°Ń†ŃŒ файлы" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Š—Š°Ń…Š°Š²Š°Ń†ŃŒ файлы ў" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Абагульванне" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "ŠŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚Š°Ń€ прылаГы" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "ŠŠæŠ°Š²ŃŃˆŃ‡ŃŠ½Š½Šµ пра нізкі ŃžŠ·Ń€Š¾Š²ŠµŠ½ŃŒ Š·Š°Ń€Š°Š“Ńƒ Š°ŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚Š°Ń€Š°" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "ŠŠæŠ°Š²ŃŃˆŃ‡Š°Ń†ŃŒ пра заГаГзены ŃžŠ·Ń€Š¾Š²ŠµŠ½ŃŒ Š·Š°Ń€Š°Š“Ńƒ" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "ŠŠæŠ°Š²ŃŃˆŃ‡ŃŠ½Š½Šµ пра ŠæŠ¾ŃžŠ½Š°ŃŃ†ŃŽ зараГжаны Š°ŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚Š°Ń€" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Š”Ń–ŃŃ‚ŃŠ¼Š½Ń‹ Š°ŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚Š°Ń€" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "ŠŠ±Š°Š³ŃƒŠ»ŃŒŠ²Š°Ń†ŃŒ ŃŃ‚Š°Ń‚Ń‹ŃŃ‚Ń‹ŠŗŃƒ" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "ŠŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚Š°Ń€" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "ŠšŠ°Š¼Š°Š½Š“Ń‹" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Š”Š°Š“Š°Ń†ŃŒ каманГу" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "ŠŠ±Š°Š³ŃƒŠ»ŃŒŠ²Š°Ń†ŃŒ Š°ŠæŠ°Š²ŃŃˆŃ‡ŃŠ½Š½Ń–" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "ŠŠ±Š°Š³ŃƒŠ»ŃŒŠ²Š°Ń†ŃŒ, калі Š°ŠŗŃ‚Ń‹ŃžŠ½Ń‹" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "ŠŸŃ€Š°Š³Ń€Š°Š¼Ń‹" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "ŠŠæŠ°Š²ŃŃˆŃ‡ŃŠ½Š½Ń–" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "ŠšŠ°Š½Ń‚Š°ŠŗŃ‚Ń‹" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Š£Š²Š°Ń…Š¾Š“Š½Ń‹Ń выклікі" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Š“ŃƒŠŗ" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "ŠŸŃ€Ń‹ŠæŃ‹Š½Ń–Ń†ŃŒ прайграванне" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Š’Ń‹Ń…Š¾Š“Š½Ń‹Ń выклікі" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "ŠŠ“ŠŗŠ»ŃŽŃ‡Ń‹Ń†ŃŒ мікрафон" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Š¢ŃŠ»ŠµŃ„Š°Š½Ń–Ń" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Š”ŠæŠ°Š»ŃƒŃ‡ŃŠ½Š½Ń– ŠŗŠ»Š°Š²Ń–Ńˆ Š“Š»Ń Š“Š·ŠµŃŠ½Š½ŃŃž" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Š”ŠŗŃ–Š½ŃƒŃ†ŃŒ ŃƒŃŠµā€¦" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Š”ŠæŠ°Š»ŃƒŃ‡ŃŠ½Š½Ń– ŠŗŠ»Š°Š²Ń–Ńˆ" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Š£Š±ŃƒŠ“Š¾Š²Ń‹" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Š­ŠŗŃŠæŠµŃ€Ń‹Š¼ŠµŠ½Ń‚Š°Š»ŃŒŠ½Ń‹Ń" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "ŠšŃŃˆ прылаГы" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Ачыстка ŠŗŃŃˆŃƒā€¦" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "ŠŸŠ°Š“Ń‚Ń€Ń‹Š¼ŠŗŠ° SMS (Ń€Š°Š½ŠµŠ¹ŃˆŠ°Ń Š²ŠµŃ€ŃŃ–Ń)" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "ŠŃžŃ‚Š°ŠæŠ°Š“Š»ŃƒŃ‡ŃŠ½Š½Šµ SFTP" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "ŠŸŠ°ŃˆŃ‹Ń€Š°Š½Ń‹Ń" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Š”ŠæŠ°Š»ŃƒŃ‡ŃŠ½Š½Ń– ŠŗŠ»Š°Š²Ń–Ńˆ" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "ŠŠ°Š»Š°Š“Ń‹ прылаГы" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Š”ŠæŠ°Š»ŃƒŃ‡Ń‹Ń†ŃŒ" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "ŠŸŃ€Ń‹Š»Š°Š“Š° Ń€Š°Š·Š»ŃƒŃ‡Š°Š½Š°" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Š’Ń‹ можаце Š½Š°Š»Š°Š“Š·Ń–Ń†ŃŒ Š³ŃŃ‚Ńƒ ŠæŃ€Ń‹Š»Š°Š“Ńƒ пераГ ŃŠæŠ°Š»ŃƒŃ‡ŃŠ½Š½ŠµŠ¼" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Š†Š½Ń„Š°Ń€Š¼Š°Ń†Ń‹Ń пра ŃˆŃ‹Ń„Ń€Š°Š²Š°Š½Š½Šµ" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Š Š°Š·Š»ŃƒŃ‡Ń‹Ń†ŃŒ" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "ŠŠ° ŠæŃ€Ń‹Š»Š°Š“Ńƒ" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Š— прылаГы" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "ŠŃ–Ń‡Š¾Š³Š°" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "ŠŠ“Š½Š°Š²Ń–Ń†ŃŒ" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "ŠŃ–Š¶ŃŠ¹ŃˆŃ‹" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Š’Ń‹ŠŗŠ»ŃŽŃ‡Ń‹Ń†ŃŒ гук" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Š—Š°Š“Š°Ń†ŃŒ" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "ŠŠ°Ń†Ń–ŃŠ½Ń–Ń†Šµ Esc, каб ŃŠŗŠ°ŃŠ°Š²Š°Ń†ŃŒ або ŠŸŃ€Š°Š±ŠµŠ», каб ŃŠŗŃ–Š½ŃƒŃ†ŃŒ ŃŠæŠ°Š»ŃƒŃ‡ŃŠ½Š½Šµ ŠŗŠ»Š°Š²Ń–Ńˆ." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "ŠŠ°Š·Š²Š° прылаГы" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_ŠŸŠµŃ€Š°Š¹Š¼ŠµŠ½Š°Š²Š°Ń†ŃŒ" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "ŠŠ±Š½Š°Š²Ń–Ń†ŃŒ" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "ŠŠ°Š»Š°Š“Ń‹ прылаГ" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Š”ŃŃ€Š²Ń–ŃŠ½Š°Šµ Š¼ŠµŠ½ŃŽ" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "ŠœŠµŠ½ŃŽ прылаГы" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Š ŃŠ“Š°Š³Š°Š²Š°Ń†ŃŒ назву прылаГы" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "ŠŸŃ€Ń‹Š»Š°Š“Ń‹" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Пошук прылаГ…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "ŠŸŠ°ŃˆŃ‹Ń€ŃŠ½Š½Ń– Š“Š»Ń Š±Ń€Š°ŃžŠ·ŠµŃ€Š°" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Š£ŠŗŠ»ŃŽŃ‡Š°Š½Š°" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Š“ŃŃ‚Š° прылаГа Š½ŃŠ±Š°Ń‡Š½Š°Ń Š“Š»Ń Ń€Š°Š·Š»ŃƒŃ‡Š°Š½Ń‹Ń… прылаГ" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "ЗнахоГжанне Š°Š“ŠŗŠ»ŃŽŃ‡Š°Š½Š°" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Š ŃŠ¶Ń‹Š¼ паказу" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "ŠŸŠ°Š½ŃŠ»ŃŒ" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "ŠœŠµŠ½ŃŽ ŠŗŠ°Ń€Ń‹ŃŃ‚Š°Š»ŃŒŠ½Ń–ŠŗŠ°" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Š”Ń‚Š²Š°Ń€Ń‹Ń†ŃŒ Š¶ŃƒŃ€Š½Š°Š» Š“Š»Ń паГтрымкі" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "ŠŸŃ€Š° GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Š’Ń‹Š±Ń€Š°Ń†ŃŒ ŠæŃ€Ń‹Š»Š°Š“Ńƒ" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Š’Ń‹Š±Ń€Š°Ń†ŃŒ" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "ŠŠµ знойГзена прылаГ" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Дпіс прылаГ" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "ДправазГача" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "ŠŠµŃˆŃ‚Š° пайшло не так" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "Š£ GSConnect Š°Š“Š±Ń‹Š»Š°ŃŃ Š½ŠµŃ‡Š°ŠŗŠ°Š½Š°Ń памылка. ŠŸŠ°Š²ŠµŠ“Š°Š¼Ń–Ń†Šµ аб праблеме разам Š· Š»ŃŽŠ±Š¾Š¹ Ń–Š½Ń„Š°Ń€Š¼Š°Ń†Ń‹ŃŠ¹, ŃŠŗŠ°Ń можа Гапамагчы." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Š¢ŃŃ…Š½Ń–Ń‡Š½Ń‹Ń ŠæŠ°Š“Ń€Š°Š±ŃŠ·Š½Š°ŃŃ†Ń–" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "ŠŠ“ŠæŃ€Š°Š²Ń–Ń†ŃŒ на Š¼Š°Š±Ń–Š»ŃŒŠ½ŃƒŃŽ ŠæŃ€Ń‹Š»Š°Š“Ńƒ" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "ŠœŠ°Š±Ń–Š»ŃŒŠ½Ń‹Ń прылаГы" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Š£ŠŗŠ»ŃŽŃ‡Ń‹Ń†ŃŒ" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d ŠæŠ°Š“Š»ŃƒŃ‡Š°Š½Š°" msgstr[1] "%d ŠæŠ°Š“Š»ŃƒŃ‡Š°Š½Ń‹" msgstr[2] "%d ŠæŠ°Š“Š»ŃƒŃ‡Š°Š½Š°" msgstr[3] "%d ŠæŠ°Š“Š»ŃƒŃ‡Š°Š½Š°" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Š’Ń‹ŠŗŠ»ŃŽŃ‡Ń‹Ń†ŃŒ" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Š ŃŠ“Š°Š³Š°Š²Š°Ń†ŃŒ" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Š’Ń‹Š“Š°Š»Ń–Ń†ŃŒ" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "ŠŠ“ŠŗŠ»ŃŽŃ‡Š°Š½Š°" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Š£Š²ŃŠ“Š·Ń–Ń†Šµ Š½Š¾Š²Ń‹Ń ŃŠæŠ°Š»ŃƒŃ‡ŃŠ½Š½Ń–, каб Š·Š¼ŃŠ½Ń–Ń†ŃŒ %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s ужо Š²Ń‹ŠŗŠ°Ń€Ń‹ŃŃ‚Š¾ŃžŠ²Š°ŠµŃ†Ń†Š°" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "ŠŸŠ°ŃžŠ½Š°Š²Š°Ń€Ń‚Š°ŃŠ½Š°Ń Ń€ŃŠ°Š»Ń–Š·Š°Ń†Ń‹Ń KDE Connect Š“Š»Ń GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "Maksim KrapiÅ­ka " #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "ŠŸŠ°Š²ŠµŠ“Š°Š¼Š»ŠµŠ½Š½Ń– аГлаГкі былі запісаны ў Š¶ŃƒŃ€Š½Š°Š». Зрабіце Š»ŃŽŠ±Ń‹Ń крокі Š½ŠµŠ°Š±Ń…Š¾Š“Š½Ń‹Ń Š“Š»Ń ŃžŠ·Š½Š°ŃžŠ»ŠµŠ½Š½Ń праблемы і затым ŠæŃ€Š°Š³Š»ŃŠ“зіце Š¶ŃƒŃ€Š½Š°Š»." #: src/preferences/service.js:414 msgid "Review Log" msgstr "ŠŸŃ€Š°Š²ŠµŃ€Ń‹Ń†ŃŒ Š¶ŃƒŃ€Š½Š°Š»" #: src/preferences/service.js:482 msgid "Laptop" msgstr "ŠŠ¾ŃžŃ‚Š±ŃƒŠŗ" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Дмартфон" #: src/preferences/service.js:486 msgid "Tablet" msgstr "ŠŸŠ»Š°Š½ŃˆŃŃ‚" #: src/preferences/service.js:488 msgid "Television" msgstr "Š¢ŃŠ»ŠµŠ±Š°Ń‡Š°Š½Š½Šµ" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Š Š°Š·Š»ŃƒŃ‡Š°Š½Š°" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "ŠŠ“Š»ŃƒŃ‡Š°Š½Š°" #: src/preferences/service.js:518 msgid "Connected" msgstr "ŠŸŠ°Š“Š»ŃƒŃ‡Š°Š½Š°" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "Чаканне ŃŃŃ€Š²Ń–ŃŃƒā€¦" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "ŠŠ°Ń†Ń–ŃŠ½Ń–Ń†Šµ Š“Š»Ń Гапамогі ў Š²Ń‹ŠæŃ€Š°ŃžŠ»ŠµŠ½Š½Ń– непалаГак" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "ŠŠ°Ń†Ń–ŃŠ½Ń–Ń†Šµ Š“Š»Ń большай інфармацыі" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "ŠŠ°Š±Ń€Š°Ń†ŃŒ Š½ŃƒŠ¼Š°Ń€" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "ŠŠ±Š°Š³ŃƒŠ»Ń–Ń†ŃŒ файл" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Дпіс Š“Š°ŃŃ‚ŃƒŠæŠ½Ń‹Ń… прылаГ" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Дпіс усіх прылаГ" #: src/service/daemon.js:369 msgid "Target Device" msgstr "ŠœŃŃ‚Š°Š²Š°Ń прылаГа" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Змест ŠæŠ°Š²ŠµŠ“Š°Š¼Š»ŠµŠ½Š½Ń" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "ŠŠ“ŠæŃ€Š°Š²Ń–Ń†ŃŒ Š°ŠæŠ°Š²ŃŃˆŃ‡ŃŠ½Š½Šµ" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "ŠŠ°Š·Š²Š° праграмы ў Š°ŠæŠ°Š²ŃŃˆŃ‡ŃŠ½Š½Ń–" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Змест Š°ŠæŠ°Š²ŃŃˆŃ‡ŃŠ½Š½Ń" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Значок Š°ŠæŠ°Š²ŃŃˆŃ‡ŃŠ½Š½Ń" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "Š†Š“ŃŠ½Ń‚Ń‹Ń„Ń–ŠŗŠ°Ń‚Š°Ń€ Š°ŠæŠ°Š²ŃŃˆŃ‡ŃŠ½Š½Ń" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Фота" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "ŠŸŃ–Š½Š³" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Званок" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "ŠŠ±Š°Š³ŃƒŠ»Ń–Ń†ŃŒ ŃŠæŠ°ŃŃ‹Š»ŠŗŃƒ" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "ŠŠ±Š°Š³ŃƒŠ»Ń–Ń†ŃŒ Ń‚ŃŠŗŃŃ‚" #: src/service/daemon.js:528 msgid "Show release version" msgstr "ŠŸŠ°ŠŗŠ°Š·Š°Ń†ŃŒ Š²ŠµŃ€ŃŃ–ŃŽ праграмы" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "ŠŠµŠ“Š°ŃŃ‚ŃƒŠæŠ½Ń‹" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Bluetooth прылаГа па %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "%s аГбітак ŠæŠ°Š»ŃŒŃ†Š°:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Запыт ŃŠæŠ°Š»ŃƒŃ‡ŃŠ½Š½Ń аГ %s" #: src/service/device.js:800 msgid "Reject" msgstr "ŠŠ“Ń…Ń–Š»Ń–Ń†ŃŒ" #: src/service/device.js:805 msgid "Accept" msgstr "ŠŸŃ€Ń‹Š½ŃŃ†ŃŒ" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "ЗнахоГжанне было Š°Š“ŠŗŠ»ŃŽŃ‡Š°Š½Š° праз ŠŗŠ¾Š»ŃŒŠŗŠ°ŃŃ†ŃŒ прылаГ у Š³ŃŃ‚Š°Š¹ сетцы." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSL не знойГзены" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "ŠŸŠ¾Ń€Ń‚ ужо Š²Ń‹ŠŗŠ°Ń€Ń‹ŃŃ‚Š¾ŃžŠ²Š°ŠµŃ†Ń†Š°" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "Абмен Ń–Š½Ń„Š°Ń€Š¼Š°Ń†Ń‹ŃŠ¹ пра Š°ŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚ар" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: Š°ŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚Š°Ń€ зараГжаны" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "ЗараГжана цалкам" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "%s: Š°ŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚Š°Ń€ Š“Š°ŃŃŠ³Š½ŃƒŃž заГаГзенага ŃžŠ·Ń€Š¾ŃžŠ½ŃŽ Š·Š°Ń€Š°Š“Ńƒ" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "%d%% зараГжана" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: нізкі зараГ Š°ŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚Š°Ń€Š°" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% Š·Š°ŃŃ‚Š°Š»Š¾ŃŃ" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Š‘ŃƒŃ„ŠµŃ€ абмену" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "ŠŠ±Š°Š³ŃƒŠ»ŃŒŠ²Š°Ń†ŃŒ змесціва Š±ŃƒŃ„ера абмену" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "ŠŠ“ŠæŃ€Š°Š²Ń–Ń†ŃŒ Š±ŃƒŃ„ŠµŃ€ абмену" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "Š—Š°ŠæŃ€Š°ŃŃ–Ń†ŃŒ Š±ŃƒŃ„ŠµŃ€ абмену" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "Š”Š¾ŃŃ‚ŃƒŠæ Га ŠŗŠ°Š½Ń‚Š°ŠŗŃ‚Š°Ńž ŃŠæŠ°Š»ŃƒŃ‡Š°Š½Š°Š¹ прылаГы" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "ŠŠ“ŃˆŃƒŠŗŠ°Ń†ŃŒ мой Ń‚ŃŠ»ŠµŃ„Š¾Š½" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "ŠŠ“Š³ŃƒŠŗŠ°Ń†Ń†Š° ŃŠæŠ°Š»ŃƒŃ‡Š°Š½Š°Š¹ прылаГай" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "ТачпаГ" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "Š”Š°Š·Š²Š°Š»ŃŠµ ŃŠæŠ°Š»ŃƒŃ‡Š°Š½Š°Š¹ прылаГзе Š“Š·ŠµŠ¹Š½Ń–Ń‡Š°Ń†ŃŒ ŃŠŗ аГГаленай Š¼Ń‹ŃˆŃˆŃƒ і ŠŗŠ»Š°Š²Ń–ŃŃ‚ŃƒŃ€Š°Š¹" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "ŠšŠ»Š°Š²Ń–ŃŃ‚ŃƒŃ€Š°" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "Š”Š²ŃƒŃ…Š½Š°ŠŗŃ–Ń€Š°Š²Š°Š½Š°Šµ аГГаленае кіраванне прайграваннем" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "ŠŠµŠ²ŃŠ“Š¾Š¼Ń‹" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "ŠŠ±Š°Š³ŃƒŠ»ŃŒŠ²Š°Ń†ŃŒ Š°ŠæŠ°Š²ŃŃˆŃ‡ŃŠ½Š½Ń– са ŃŠæŠ°Š»ŃƒŃ‡Š°Š½Ń‹Š¼Ń– прылаГамі" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Š”ŠŗŠ°ŃŠ°Š²Š°Ń†ŃŒ Š°ŠæŠ°Š²ŃŃˆŃ‡ŃŠ½Š½Šµ" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Š—Š°ŠŗŃ€Ń‹Ń†ŃŒ Š°ŠæŠ°Š²ŃŃˆŃ‡ŃŠ½Š½Šµ" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "ŠŠ“ŠŗŠ°Š·Š°Ń†ŃŒ на Š°ŠæŠ°Š²ŃŃˆŃ‡ŃŠ½Š½Šµ" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "ŠŠŗŃ‚Ń‹Š²Š°Š²Š°Ń†ŃŒ Š°ŠæŠ°Š²ŃŃˆŃ‡ŃŠ½Š½Šµ" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "Š—Š°ŠæŃ‹Ń‚Š²Š°Ń†ŃŒ ŃŠæŠ°Š»ŃƒŃ‡Š°Š½ŃƒŃŽ ŠæŃ€Ń‹Š»Š°Š“Ńƒ на Š·Š“Ń‹Š¼ŠŗŃƒ фота і ŠæŠµŃ€Š°Š“Š°Ń‡Ńƒ ŃŠ³Š¾ на Š³ŃŃ‚Ń‹ ПК" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Збой пераГачы" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Збой Š°Š“ŠæŃ€Š°ŃžŠŗŃ– ā€œ%sā€ у %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "ŠŠ“ŠæŃ€Š°ŃžŠ»ŃŃ†ŃŒ і Š°Ń‚Ń€Ń‹Š¼Š»Ń–Š²Š°Ń†ŃŒ пінг" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "ŠŸŃ–Š½Š³: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "ŠŸŃ€ŃŠ·ŠµŠ½Ń‚Š°Ń†Ń‹Ń" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "Š’Ń‹ŠŗŠ°Ń€Ń‹ŃŃ‚Š¾ŃžŠ²Š°Ń†ŃŒ ŃŠæŠ°Š»ŃƒŃ‡Š°Š½ŃƒŃŽ ŠæŃ€Ń‹Š»Š°Š“Ńƒ Š“Š»Ń ŠæŃ€ŃŠ·ŠµŠ½Ń‚Š°Ń†Ń‹Ń–" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Š—Š°ŠæŃƒŃŠŗŠ°Ń†ŃŒ каманГы" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "Š—Š°ŠæŃƒŃŠŗŠ°Ń†ŃŒ каманГы на ŃŠæŠ°Š»ŃƒŃ‡Š°Š½Š°Š¹ прылаГзе або Š“Š°Š·Š²Š¾Š»Ń–Ń†ŃŒ ёй Š·Š°ŠæŃƒŃŠŗŠ°Ń†ŃŒ ŠæŃ€Š°Š“Š²Ń‹Š·Š½Š°Ń‡Š°Š½Ń‹Ń каманГы на Š³ŃŃ‚ым ПК" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "ŠŸŃ€Š°Š³Š»ŃŠ“Š°Ń†ŃŒ Ń„Š°Š¹Š»Š°Š²ŃƒŃŽ ŃŃ–ŃŃ‚ŃŠ¼Ńƒ ŃŠæŠ°Š»ŃƒŃ‡Š°Š½Š°Š¹ прылаГы" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "ŠŸŠ°Š“Š»ŃƒŃ‡Ń‹Ń†ŃŒ" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "ŠŠ“Š»ŃƒŃ‡Ń‹Ń†ŃŒ" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s ŠæŠ°Š²ŠµŠ“Š°Š¼Ń–Ńž пра ŠæŠ°Š¼Ń‹Š»ŠŗŃƒ" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "ŠŠ±Š°Š³ŃƒŠ»Ń–Ń†ŃŒ" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "ŠŠ±Š°Š³ŃƒŠ»ŃŒŠ²Š°Ń†ŃŒ файлы і спасылкі паміж прылаГамі" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s не Š“Š°Š·Š²Š°Š»ŃŠµ Š·Š°ŠæŠ°Š¼ŠæŠ¾ŃžŠ²Š°Ń†ŃŒ файлы" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "ŠŸŠµŃ€Š°Š“Š°Ń‡Š° файла" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "Атрыманне ā€œ%sā€ Š· %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "ŠŸŠ°ŃŠæŃŃ…Š¾Š²Š° пераГаГзена" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "Атрымана ā€œ%sā€ Š· %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "ŠŠ“ŠŗŃ€Ń‹Ń†ŃŒ папку" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "ŠŠ“ŠŗŃ€Ń‹Ń†ŃŒ файл" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "ŠŠµ ŃžŠ“Š°Š»Š¾ŃŃ Š°Ń‚Ń€Ń‹Š¼Š°Ń†ŃŒ ā€œ%sā€ Š· %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Š¢ŃŠŗŃŃ‚ Š°Š±Š°Š³ŃƒŠ»ŠµŠ½Ń‹ %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "ŠŠ“ŠæŃ€Š°ŃžŠŗŠ° ā€œ%sā€ у %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "ŠŠ“ŠæŃ€Š°ŃžŠ»ŠµŠ½Š° ā€œ%sā€ у %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "ŠŠ“ŠæŃ€Š°Š²Ń–Ń†ŃŒ файлы ў %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "ŠŠ“ŠŗŃ€Ń‹Ń†ŃŒ па ŃŠŗŠ°Š½Ń‡ŃŠ½Š½Ń–" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "ŠŠ“ŠæŃ€Š°Š²Ń–Ń†ŃŒ ŃŠæŠ°ŃŃ‹Š»ŠŗŃƒ ў %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "ŠŠ“ŠæŃ€Š°ŃžŠ»ŃŃ†ŃŒ і Ń‡Ń‹Ń‚Š°Ń†ŃŒ SMS на ŃŠæŠ°Š»ŃƒŃ‡Š°Š½Š°Š¹ прылаГзе, Š°ŠæŠ°Š²ŃŃˆŃ‡Š°Ń†ŃŒ пра Š½Š¾Š²Ń‹Ń SMS" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "ŠŠ¾Š²Š°Šµ SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "ŠŠ“ŠŗŠ°Š·Š°Ń†ŃŒ на SMS" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "ŠŠ±Š°Š³ŃƒŠ»Ń–Ń†ŃŒ SMS" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Š”Ń–ŃŃ‚ŃŠ¼Š½Š°Ń Š³ŃƒŃ‡Š½Š°ŃŃ†ŃŒ" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "Š”Š°Š·Š²Š¾Š»Ń–Ń†ŃŒ ŃŠæŠ°Š»ŃƒŃ‡Š°Š½Š°Š¹ прылаГзе ŠŗŃ–Ń€Š°Š²Š°Ń†ŃŒ Š³ŃƒŃ‡Š½Š°ŃŃ†ŃŽ ŃŃ–ŃŃ‚ŃŠ¼Ń‹" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio не знойГзены" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "ŠŠæŠ°Š²ŃŃˆŃ‡Š°Ń†ŃŒ пра выклікі і Ń€ŃŠ³ŃƒŠ»ŃŠ²Š°Ń†ŃŒ Š³ŃƒŃ‡Š½Š°ŃŃ†ŃŒ ŃŃ–ŃŃ‚ŃŠ¼Ń‹ паГчас Š²Ń‹ŠŗŠ»Ń–ŠŗŠ°Ńž" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "АГкл. гук Š²Ń‹ŠŗŠ»Ń–ŠŗŃƒ" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "ŠŠµŠ²ŃŠ“Š¾Š¼Ń‹ кантакт" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "УвахоГны выклік" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "ВыхоГны выклік" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Факс" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "ŠŸŃ€Š°Ń†Š¾ŃžŠ½Ń‹" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "ŠœŠ°Š±Ń–Š»ŃŒŠ½Ń‹" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Š”Š°Š¼Š°ŃˆŠ½Ń–" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "ŠŠ“ŠæŃ€Š°Š²Ń–Ń†ŃŒ у %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Š¢Š¾Š»ŃŒŠŗŃ– ŃˆŃ‚Š¾" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Š£Ń‡Š¾Ń€Š°ćƒ»%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d хвіліна" msgstr[1] "%d хвіліны" msgstr[2] "%d хвілін" msgstr[3] "%d хвіліны" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Š“Ń€ŃƒŠæŠ°Š²Š¾Šµ павеГамленне" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Š’Ń‹: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "І яшчэ %d кантакт" msgstr[1] "І %d Ń–Š½ŃˆŃ‹Ń кантакты" msgstr[2] "І %d Ń–Š½ŃˆŃ‹Ń… ŠŗŠ°Š½Ń‚Š°ŠŗŃ‚Š°Ńž" msgstr[3] "І %d Ń–Š½ŃˆŃ‹Ń…" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "ŠŠ“Š“Š°Š»ŠµŠ½Š°Ń ŠŗŠ»Š°Š²Ń–ŃŃ‚ŃƒŃ€Š° %s не Š°ŠŗŃ‚Ń‹ŃžŠ½Š°Ń" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (ŠŃ†ŃŠ½ŠŗŠ°ā€¦)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d∶%02d Да ŠæŠ¾ŃžŠ½Š°Š³Š° Š·Š°Ń€Š°Š“Ńƒ)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d∶%02d застаецца)" #: src/shell/notification.js:54 msgid "Reply" msgstr "ŠŠ“ŠŗŠ°Š·Š°Ń†ŃŒ" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "ŠŠ±Š°Š³ŃƒŠ»ŃŒŠ²Š°Š¹Ń†Šµ спасылкі Š· GSConnect, Š½Š°ŠæŃ€Š°Š¼ŃƒŃŽ ў Š±Ń€Š°ŃžŠ·ŠµŃ€ або праз SMS." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Š”ŃŃ€Š²Ń–Ń Š½ŠµŠ“Š°ŃŃ‚ŃƒŠæŠ½Ń‹" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "ŠŠ“ŠŗŃ€Ń‹Ń†ŃŒ у Š±Ń€Š°ŃžŠ·ŠµŃ€Ń‹" gnome-shell-extension-gsconnect-50/po/ca.po000066400000000000000000001022121421543444100210460ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2022-02-28 08:39\n" "Last-Translator: \n" "Language-Team: Catalan\n" "Language: ca_ES\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: ca\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "Implementació del KDE Connect per al GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "L’equip del GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "Amb el GSConnect podeu connectar-vos de forma segura a dispositius mòbils i altres escriptoris per a:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Compartir fitxers, enllaƧos i text" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Enviar i rebre missatges" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Sincronitzar el contingut del porta-retalls" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Sincronitzar els contactes" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Sincronitzar les notificacions" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Controlar reproductors multimĆØdia" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "Controlar el volum del sistema" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Executar ordres predefinides" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "I mĆ©s…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Connecta amb…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "CancelĀ·la" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Connecta" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "AdreƧa IP" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "No hi ha cap contacte" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Ajuda" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "IntroduĆÆu un nĆŗmero telefònic o un nom" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "Envia un SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Envia" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "El dispositiu estĆ  desconnectat" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Envia el missatge" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Escriviu un missatge" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Missatgeria" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Conversa nova" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "No hi ha cap conversa" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "No s’ha seleccionat cap conversa" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Seleccioneu una conversa o inicieu-ne una de nova" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Edita l’ordre" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Desa" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Nom" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "LĆ­nia d’ordres" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Trieu un executable" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Obre" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Ordinador d’escriptori" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "CĆ mera" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Sincronització del porta-retalls" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Reproductors multimĆØdia" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "RatolĆ­ i teclat" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Control de volum" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Fitxers" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Desa els fitxers a" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Compartició" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Bateria del dispositiu" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Notificació de bateria baixa" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Notificació de bateria carregada" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Bateria del sistema" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Comparteix estadĆ­stiques" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Bateria" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Ordres" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Afegeix una ordre" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Notificacions de compartició" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Comparteix quan estigui actiu" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Aplicacions" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Notificacions" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Contactes" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Trucades entrants" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Volum" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Posa en pausa la multimĆØdia" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Trucades en curs" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Silencia el micròfon" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Telefonia" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Dreceres d’accions" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Reinicialitza-ho tot…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Dreceres" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Connectors" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Experiments" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Memòria cau del dispositiu" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Neteja la memòria cau…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Compatibilitat d’SMS llegat" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "Muntatge automĆ tic d’SFTP" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "AvanƧat" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Dreceres de teclat" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "ParĆ metres del dispositiu" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Aparella" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "No s’ha aparellat el dispositiu" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Podeu configurar el dispositiu abans d’aparellar-lo" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Informació de xifratge" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Desaparella" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Al dispositiu" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Des del dispositiu" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Res" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Restaura" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Abaixa" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Silencia" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Defineix" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Premeu Esc per a cancelĀ·lar o RetrocĆ©s per a reinicialitzar la drecera de teclat." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Nom del dispositiu" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_Canvia el nom" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Actualitza" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "ParĆ metres del mòbil" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "MenĆŗ de serveis" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "MenĆŗ d’aparells" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Edita el nom del dispositiu" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Dispositius" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "S’estan cercant dispositius…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Connectors per als navegadors" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Activa" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Aquest dispositiu Ć©s invisible als dispositius no aparellats" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Descobriment deshabilitat" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Mode de visualització" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Plafó" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "MenĆŗ d’usuari" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Genera un registre d'assistĆØncia" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "Quant al GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Seleccioneu un dispositiu" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Selecciona" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "No s’ha trobat cap dispositiu" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Llista de dispositius" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Alguna cosa ha anat malament" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "" #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Detalls tĆØcnics" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "Envia al dispositiu mòbil" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Dispositius mòbils" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Activa" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d connectat" msgstr[1] "%d connectats" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Desactiva" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Edita" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Elimina" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Desactivat" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s ja s’estĆ  fent servir" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Una implementació completa de KDE Connect per al GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "Adolfo Jayme Barrientos , 2019-2020\n" "Marc Riera Irigoyen , 2019" #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "S’estan registrant els missatges de depuració. Efectueu els passos necessaris per a reproduir un problema i, tot seguit, reviseu el registre." #: src/preferences/service.js:414 msgid "Review Log" msgstr "Mostra el registre" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Ordinador portĆ til" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "TelĆØfon intelĀ·ligent" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tauleta" #: src/preferences/service.js:488 msgid "Television" msgstr "Televisió" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Desaparellat" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Desconnectat" #: src/preferences/service.js:518 msgid "Connected" msgstr "Connectat" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "S’estĆ  esperant el servei…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "Ajuda per a resoldre el problema" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "MĆ©s informació" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Comparteix un fitxer" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Enumera els dispositius disponibles" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Enumera tots els dispositius" #: src/service/daemon.js:369 msgid "Target Device" msgstr "Dispositiu de destinació" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Cos del missatge" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Envia una notificació" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "Nom d’aplicació de la notificació" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Corps de la notificació" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Icona de notificació" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "Identificador de la notificació" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Foto" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Comprovació de connectivitat" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Truca" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Comparteix un enllaƧ" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Comparteix text" #: src/service/daemon.js:528 msgid "Show release version" msgstr "" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "No disponible" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Dispositiu Bluetooth a %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "Empremta de l’aparell %s:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "SolĀ·licitud d’aparellament d’un %s" #: src/service/device.js:800 msgid "Reject" msgstr "Rebutja-la" #: src/service/device.js:805 msgid "Accept" msgstr "Accepta-la" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "" #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "No s’ha trobat l’OpenSSL" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "CĆ rrega completa" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% restant" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Porta-retalls" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Troba el meu telĆØfon" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Teclat" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "Desconegut" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Ha fallat la transferĆØncia" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "No s’ha pogut enviar Ā«%sĀ» al %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Comprovació de connectivitat: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "Presentació" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Execució d’ordres" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Munta" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Desmunta" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Compartició" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "S’estĆ  rebent Ā«%sĀ» del %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "La transferĆØncia ha estat exitosa" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "S’ha rebut Ā«%sĀ» del %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Obre la carpeta" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Obre el fitxer" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "No s’ha pogut rebre Ā«%sĀ» del %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Text compartit per %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "S’estĆ  enviant Ā«%sĀ» al %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "S’ha enviat Ā«%sĀ» al %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Envia fitxers al %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Obre’l en finalitzar" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Envia un enllaƧ al %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "SMS nou (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Volum del sistema" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "No s’ha trobat el PulseAudio" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Silencia la trucada" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Contacte desconegut" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Trucada entrant" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Trucada en curs" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Fax" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Feina" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Mòbil" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Particular" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Envia al %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Ara mateix" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Ahir惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minut" msgstr[1] "%d minuts" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Missatge de grup" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Vós: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "I %d altre contacte" msgstr[1] "I %d d’altres" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (en estimació…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d∶%02d restants)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Respon" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Compartiu enllaƧos amb el GSConnect, directament al navegador o mitjanƧant SMS." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "El servei no estĆ  disponible" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Obre al navegador" gnome-shell-extension-gsconnect-50/po/cs.po000066400000000000000000001064101421543444100210740ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-09 01:51\n" "Last-Translator: \n" "Language-Team: Czech\n" "Language: cs_CZ\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 3;\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: cs\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "Implementace KDE Connect pro GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "Kolektiv GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnect je kompletnĆ­ implementace nĆ”stroje KDE Connect, určenĆ” zejmĆ©na pro prostředĆ­ GNOME Shell s napojenĆ­m pro sprĆ”vce souborÅÆ Nautilus a dĆ”le pro webovĆ© prohlížeče Chrome a Firefox. Tým KDE Connect mĆ” aplikace pro systĆ©my Linux, BSD, Android, Sailfish, macOS a Windows." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "PomocĆ­ GSConnect se můžete bezpečně připojit k mobilnĆ­m zařízenĆ­m a ostatnĆ­m stolnĆ­m počƭtaÄÅÆm a:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "SdĆ­let soubory, odkazy a text" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "OdesĆ­lat a přijĆ­mat zprĆ”vy" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Synchronizovat obsah schrĆ”nky" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Synchronizovat kontakty" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Synchronizovat oznĆ”menĆ­" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "OvlĆ”dat přehrĆ”vĆ”nĆ­ multimĆ©diĆ­" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "OvlĆ”dat hlasitost systĆ©mu" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "SpouÅ”tět předpřipravenĆ© příkazy" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "A dalŔí…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect v GNOME Shell" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Připojit k…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Storno" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Připojit" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP adresa" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "ŽÔdnĆ© kontakty" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "NĆ”pověda" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "NapiÅ”te telefonnĆ­ čƭslo nebo jmĆ©no" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "OstatnĆ­" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "Poslat SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Poslat" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "ZařízenĆ­ je odpojeno" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Odeslat zprĆ”vu" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Napsat zprĆ”vu" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "ZadĆ”nĆ­ zprĆ”vy" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "NapiÅ”te zprĆ”vu a stisknutĆ­m klĆ”vesy Enter ji odeÅ”lete" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "ZasĆ­lĆ”nĆ­ zprĆ”v" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "NovĆ” konverzace" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "ŽÔdnĆ© konverzace" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "NevybrĆ”na žÔdnĆ” konverzace" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Vybrat nebo zahĆ”jit konverzaci" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Upravit příkaz" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Uložit" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "NĆ”zev" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Příkazový řÔdek" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Vyberte spustitelný soubor" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Otevřít" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Počƭtač" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "FotoaparĆ”t" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Synchronizace schrĆ”nky" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "PřehrĆ”vače mĆ©diĆ­" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "MyÅ” a klĆ”vesnice" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "OvlĆ”dĆ”nĆ­ hlasitosti" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Soubory" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Přijmout soubory" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Uložit soubory do" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "SdĆ­lenĆ­" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "AkumulĆ”tor zařízenĆ­" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "UpozorněnĆ­ na tĆ©měř vybitý akumulĆ”tor" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "Změněno na uživatelsky určenou Ćŗroveň upozorňovĆ”nĆ­" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "OznĆ”menĆ­ o ĆŗplnĆ©m dobitĆ­" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "SystĆ©mový akumulĆ”tor" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "SdĆ­let statistiky" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "AkumulĆ”tor" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Příkazy" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Přidat příkaz" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "SdĆ­let oznĆ”menĆ­" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "SdĆ­let když je aktivnĆ­" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Aplikace" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "OznĆ”menĆ­" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Kontakty" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "PříchozĆ­ hovory" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Hlasitost" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Pozastavit multimĆ©dia" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "ProbĆ­hajĆ­cĆ­ hovory" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Ztlumit mikrofon" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Telefonie" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "KlĆ”vesovĆ© zkratky akcĆ­" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "VrĆ”tit vÅ”e na výchozĆ­ hodnoty…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "KlĆ”vesovĆ© zkratky" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "ZĆ”suvnĆ© moduly" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "ExperimentĆ”lnĆ­" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Mezipaměń zařízenĆ­" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Vymazat mezipaměń…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Podpora pÅÆvodnĆ­ch SMS zprĆ”v" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "SFTP automatickĆ© připojenĆ­" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "PokročilĆ©" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "KlĆ”vesovĆ© zkratky" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "NastavenĆ­ zařízenĆ­" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "SpĆ”rovĆ”nĆ­" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "ZařízenĆ­ nenĆ­ spĆ”rovĆ”no" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Před spĆ”rovĆ”nĆ­m může být třeba toto zařízenĆ­ nastavit" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Informace o Å”ifrovĆ”nĆ­" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "ZruÅ”it spĆ”rovĆ”nĆ­" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Do zařízenĆ­" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Ze zařízenĆ­" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Nic" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Obnovit" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Snížit" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Ztlumit" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Nastavit" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "ZruŔíte stiskem Esc nebo pomocĆ­ Backspace vrĆ”tĆ­te do pÅÆvodnĆ­ho stavu." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "NĆ”zev zařízenĆ­" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_Přejmenovat" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Obnovit" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "NastavenĆ­ telefonu" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "NabĆ­dka služeb" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "NabĆ­dka zařízenĆ­" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Upravit nĆ”zev zařízenĆ­" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "ZařízenĆ­" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "VyhledĆ”vĆ”nĆ­ zařízení…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Doplňky pro prohlížeče" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Zapnout" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Toto zařízenĆ­ je pro nespĆ”rovanĆ” zařízenĆ­ neviditelnĆ©" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "ObjevovĆ”nĆ­ vypnuto" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Režim zobrazenĆ­" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Panel" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "UživatelskĆ” nabĆ­dka" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Vytvořit zĆ”znam udĆ”lostĆ­ pro podporu" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "O GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Vybrat zařízenĆ­" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Vybrat" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Nenalezeno žÔdnĆ© zařízenĆ­" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Seznam zařízenĆ­" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "HlÔŔenĆ­" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Něco se pokazilo" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "GSConnect narazilo na neočekĆ”vanou chybu. Nahlaste prosĆ­m problĆ©m a přiložte veÅ”kerĆ© informace, kterĆ© mohou pomoci." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "TechnickĆ© podrobnosti" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "Odeslat do mobilnĆ­ho zařízenĆ­" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "MobilnĆ­ zařízenĆ­" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Zapnout" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d připojen" msgstr[1] "%d připojenĆ©" msgstr[2] "%d připojených" msgstr[3] "%d připojenĆ©" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Vypnout" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Upravit" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Odebrat" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Vypnuto" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Zadejte novou zkratku pro %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s už je používĆ”no" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "ÚplnĆ” (re)implementace KDE Connect pro GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "Pavel Borecki " #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "LadĆ­cĆ­ zprĆ”vy jsou zaznamenĆ”vĆ”ny. Podnikněte kroky, potřebnĆ© pro vyvolĆ”nĆ­ problĆ©mu a pak si zĆ”znam prohlĆ©dněte." #: src/preferences/service.js:414 msgid "Review Log" msgstr "ProhlĆ©dnout si zĆ”znam udĆ”lostĆ­" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Notebook" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Chytrý telefon" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tablet" #: src/preferences/service.js:488 msgid "Television" msgstr "Televize" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "NespĆ”rovĆ”no" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Odpojeno" #: src/preferences/service.js:518 msgid "Connected" msgstr "Připojeno" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "ČekĆ” se na službu…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "KliknutĆ­m otevřete nĆ”povědu pro odstraňovĆ”nĆ­ potíží" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "DalŔí informace zĆ­skĆ”te kliknutĆ­m" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Vytočit čƭslo" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "SdĆ­let soubor" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Vypsat zařízenĆ­ k dispozici" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Vypsat vÅ”echna zařízenĆ­" #: src/service/daemon.js:369 msgid "Target Device" msgstr "CĆ­lovĆ© zařízenĆ­" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Tělo zprĆ”vy" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Poslat oznĆ”menĆ­" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "NĆ”zev oznamovacĆ­ aplikace" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Tělo oznĆ”menĆ­" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Ikona oznĆ”menĆ­" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "Identif. oznĆ”menĆ­" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Fotka" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "VyzvĆ”něnĆ­" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "SdĆ­let odkaz" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "SdĆ­let text" #: src/service/daemon.js:528 msgid "Show release version" msgstr "Zobrazit verzi vydĆ”nĆ­" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "NenĆ­ k dispozici" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Bluetooth zařízenĆ­ na %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "Otisk %s:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "ŽÔdost o spĆ”rovĆ”nĆ­ od %s" #: src/service/device.js:800 msgid "Reject" msgstr "OdmĆ­tnout" #: src/service/device.js:805 msgid "Accept" msgstr "Přijmout" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "KvÅÆli velkĆ©mu množstvĆ­ zařízenĆ­ na tĆ©to sĆ­ti bylo objevovĆ”nĆ­ vypnuto." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSL nebylo nalezeno" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "Port už je používĆ”n" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "UpřesňujĆ­cĆ­ informace o akumulĆ”toru" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: AkumulĆ”tor je nabitý" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "Plně nabito" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "%s: AkumulĆ”tor dosĆ”hl uživatelsky určenĆ© Ćŗrovně nabitĆ­" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "%d%% nabito" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: AkumulĆ”tor je tĆ©měř vybitý" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% zbývĆ”" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "SchrĆ”nka" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "SdĆ­let obsah schrĆ”nky" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "OdesĆ­lĆ”nĆ­ schrĆ”nky" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "StahovĆ”nĆ­ schrĆ”nky" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "Přistupovat ke kontaktÅÆm na spĆ”rovanĆ©m zařízenĆ­" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "NajĆ­t mÅÆj telefon" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "Prozvonit vaÅ”e spĆ”rovanĆ© zařízenĆ­" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "MyÅ”" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "UmožnĆ­ použít spĆ”rovanĆ© zařízenĆ­ jako vzdĆ”lenou myÅ” a klĆ”vesnici" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "KlĆ”vesnice" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "ObousměrnĆ© dĆ”lkovĆ© ovlĆ”dĆ”nĆ­ přehrĆ”vĆ”nĆ­ multimĆ©diĆ­" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "NeznĆ”mĆ©" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "SdĆ­let oznĆ”menĆ­ se spĆ”rovaným zařízenĆ­m" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "ZruÅ”it oznĆ”menĆ­" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Zavřít oznĆ”menĆ­" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "Odpovědět na oznĆ”menĆ­" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Zapnout oznamovĆ”nĆ­" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "VyžÔdat si pořízenĆ­ fotky spĆ”rovaným zařízenĆ­m a přenĆ©st ji do tohoto počƭtače" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Přenos se nezdařil" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Nepodařilo se odeslat ā€ž%sā€œ do %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "Odeslat a přijmout pingy" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "Prezentace" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "Použít spĆ”rovanĆ© zařízenĆ­ jako prezentĆ”tor" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Spustit příkazy" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "Spustit příkazy na svĆ©m spĆ”rovanĆ©m zařízenĆ­ nebo nechat zařízenĆ­ spouÅ”tět předem určenĆ© příkazy na tomto počƭtači" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "ProchĆ”zet souborový systĆ©m spĆ”rovanĆ©ho zařízenĆ­" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Připojit" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Odpojit" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s nahlĆ”silo chybu" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "SdĆ­let" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "SdĆ­let soubory a URL adresy mezi zařízenĆ­mi" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s nenĆ­ umožněno nahrĆ”vat soubory" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "PřenÔŔenĆ­ souboru" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "ZĆ­skĆ”vĆ”nĆ­ ā€ž%sā€œ z %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Přenos ĆŗspěŔný" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "Obdrženo ā€ž%sā€œ z %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Otevřít složku" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Otevřít soubor" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Nepodařilo se přijmout ā€ž%sā€œ od %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Text sdĆ­lený %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "PosĆ­lĆ”nĆ­ ā€ž%sā€œ do %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "ā€ž%sā€œ odeslĆ”no do %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Odeslat soubory do %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Po dokončenĆ­ otevřít" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Poslat odkaz do %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "Poslat a čƭst SMS zprĆ”vu ze spĆ”rovanĆ©ho zařízenĆ­ a být upozorněni na novĆ© SMS" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "NovĆ” SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "Odpovědět na SMS" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "SdĆ­let SMS" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Hlasitost systĆ©mu" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "Umožnit ovlĆ”dat ze spĆ”rovanĆ©ho zařízenĆ­ hlasitost zvuku na systĆ©mu" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio nenalezeno" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "DostĆ”vejte upozorněnĆ­ na hovory a upravujte hlasitost počƭtači při vyzvĆ”něnĆ­ / probĆ­hajĆ­cĆ­ch hovorech" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Umlčet hovor" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "NeznĆ”mý kontakt" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "PříchozĆ­ hovor" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "ProbĆ­hajĆ­cĆ­ hovor" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Fax" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "PracovnĆ­" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "MobilnĆ­" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "DomÅÆ" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Poslat na %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "PrĆ”vě teď" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Včera惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minuta" msgstr[1] "%d minuty" msgstr[2] "%d minut" msgstr[3] "%d minut" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "SkupinovĆ” zprĆ”va" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Vy: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "A %d dalŔí kontakt" msgstr[1] "A %d dalŔí kontakty" msgstr[2] "A %d dalŔích kontaktÅÆ" msgstr[3] "A %d dalŔí kontakty" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "VzdĆ”lenĆ” klĆ”vesnice nenĆ­ na %s aktivnĆ­" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (odhaduje se…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d∶%02d do ĆŗplnĆ©ho nabitĆ­)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d∶%02d zbývĆ”)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Odpovědět" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "SdĆ­let odkazy pomocĆ­ GSConnect, přímo do prohlížeče nebo prostřednictvĆ­m SMS." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Služba nedostupnĆ”" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Otevřít v prohlížeči" gnome-shell-extension-gsconnect-50/po/da.po000066400000000000000000001004471421543444100210570ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-09 01:51\n" "Last-Translator: \n" "Language-Team: Danish\n" "Language: da_DK\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: da\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "GSConnect hold" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "Med GSConnect du kan tilslut sikkert til mobil enheder og andre stationƦr computer til:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Send og modtag beskeder" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Synkroniser udklipsholder indhold" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Synkroniser kontakter" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Synkroniser notifikationer" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "Og mere…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect i GNOME Shell" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Forbind til…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Annuller" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Forbind" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP adresse" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "Ingen kontakter" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "HjƦlp" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Indtast et telefonnummer eller navn" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Andre" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "Send SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Send" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Enheden er afbrudt" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Send Meddelelse" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Skriv en meddelelse" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "Besked Input" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Messaging" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Ny Samtale" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Ingen Samtaler" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Ingen samtaler valgt" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "VƦlg eller start en samtale" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Rediger Kommando" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Gem" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Navn" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Kommandolinje" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "VƦlg en eksekverbar" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "ƅbn" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "StationƦr Computer" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "Kamera" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Udklipsholder synkroniseres" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Medieafspillere" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Mus & Tastatur" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Lydstyring" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Filer" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Modtag Filer" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Gem filer til" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Deling" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Enhedens Batteri" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Lavt Batteri Notifikation" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Fuldt Opladet Notifikation" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "System Batteri" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Del Statistikker" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Batteri" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Kommandoer" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "TilfĆøj Kommando" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Del Notifikationer" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Del NĆ„r Aktiv" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Applikationer" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Notifikationer" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Kontakter" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "IndgĆ„ende Opkald" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Lydstryke" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Pause Media" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "IgangvƦrende Opkald" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Stum Mikrofon" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Telefoni" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Handlings Genveje" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Nulstil Alle…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Genveje" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Plugins" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Eksperimentel" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Enhed Cache" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Ryd Cache…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Arv SMS" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "Avanceret" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Tastaturgenveje" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "Enhed Indstillinger" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Par" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "Enheden er uparret" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Du kan konfigurere denne enhed fĆør parring" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Kryptering Info" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Koble fra en enhed" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Til Apparat" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Fra Enhed" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Intet" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Gendan" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Reducere" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "LydlĆøs" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Indstil" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Tryk pĆ„ Esc at annullere eller backspace-nĆøgle for at nulstille tastaturgenvejen." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Enheds Navn" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_OmdĆøbe" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Opdater" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "Mobile Indstillinger" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Service Menu" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "Enhedsmenu" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Rediger Enheds Navn" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Enheder" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "SĆøger efter enheder…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Webbrowser TilfĆøj-Ons" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Aktiver" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Denne enhed er usynlig til ikke parret enheder" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Discovery Deaktiveret" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Visningstilstand" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Panel" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "Bruger Menu" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Generere Underbygge Log" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "Om GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "VƦlg en mobil enhed" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "VƦlg" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Ingen Enhed Fundet" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Enhedsliste" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Anmeld" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "" #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Tekniske Detaljer" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "Send til Mobil Enhed" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Mobil Enheder" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "TƦnde For" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d Tilsuttet" msgstr[1] "%d Tilsuttet" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Sluk" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Rediger" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Fjerne" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Deaktiveret" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Indtast en ny genvej for at Ʀndre %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s er allerede bliver brugt" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "En komplet KDE Connect implementering for GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "oversƦtter-kreditter" #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "Fejlmeddelelser er logges. Tage de nĆødvendige skridt til at reproducere et problem, og gennemgĆ„ loggen." #: src/preferences/service.js:414 msgid "Review Log" msgstr "GennemgĆ„ Loggen" #: src/preferences/service.js:482 msgid "Laptop" msgstr "BƦrbar" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Smartphone" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tablet" #: src/preferences/service.js:488 msgid "Television" msgstr "Fjernsyn" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Ikke parret" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Frakoblet" #: src/preferences/service.js:518 msgid "Connected" msgstr "Tilsluttet" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "Venter pĆ„ service…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "Klik for hjƦlp fejlfinding" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "Klik for mere information" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Opkaldsnummer" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Del Fil" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Liste over tilgƦngelige enheder" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Liste alle enheder" #: src/service/daemon.js:369 msgid "Target Device" msgstr "MĆ„let Enhed" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Beskedkrop" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Send Notifikation" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Notifikation Ikon" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "Notifikation ID" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Foto" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Ring" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Del Link" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Del tekst" #: src/service/daemon.js:528 msgid "Show release version" msgstr "" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Ikke TilgƦngelig" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Bluetooth enhed pĆ„ %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "%s Fingeraftryk:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Paranmodning fra %s" #: src/service/device.js:800 msgid "Reject" msgstr "Afvise" #: src/service/device.js:805 msgid "Accept" msgstr "Acceptere" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "Opdagelse er deaktiveret pĆ„ grund af antallet af enheder pĆ„ dette netvƦrk." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSL ikke fundet" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: Batteriet er fyldt" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "Fuldt Opladet" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: Batteriet er lavt" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% tilbage" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Udklipsholder" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "Udklipsholder Tryk" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "Udklipsholder TrƦkke" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Find min telefon" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "MusemĆ„tte" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Tastatur" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "Ukendt" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Annullere Besked" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Luk Besked" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "Svar Notifikation" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Aktiver Notifikation" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "OverfĆørsel mislykkedes" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Kunne ikke send \"%s\" til %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "PrƦsentation" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "UdfĆøre Kommando" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Monter" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Afmonter" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Del" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s er ikke tilladt at uploade filer" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "OverfĆører Fil" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "Modtager \"%s\" fra %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "OverfĆørsel Succesfuld" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "Modtaget \"%s\" fra %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "ƅbn mappe" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "ƅben fil" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Kunne ikke modtage \"%s\" fra %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Tekst Deles Af %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Sende \"%s\" til %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "Sendt \"%s\" til %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Send filer til %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "ƅbne, nĆ„r fƦrdig" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Send et link til %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Ny SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "Besvar SMS" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "Del SMS" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Systemlydstyrke" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio ikke fundet" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Stum Opkald" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Ukendt Kontakt" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "IndgĆ„ende Opkald" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "IgangvƦrende Opkald" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Fax" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Arbejde" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Mobil" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Hjem" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Send til %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Lige Nu" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "I gĆ„r惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minut" msgstr[1] "%d minutter" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Gruppebesked" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Du: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "Og %d andre kontakt" msgstr[1] "Og %d andre" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (Estimering…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d:%02d Indtil Fuld)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d:%02d Resterende)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Svar" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Dele links med GSConnect, direkte til browseren eller via SMS." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Tjenesten er ikke tilgƦngelig" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "ƅbn i browser" gnome-shell-extension-gsconnect-50/po/de.po000066400000000000000000001055761421543444100210730ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-09 01:51\n" "Last-Translator: \n" "Language-Team: German\n" "Language: de_DE\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: de\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "KDE Connect-Implementierung für GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "GSConnect-Team" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnect ist eine vollstƤndige Implementierung von KDE Connect für GNOME Shell, insbesondere mit Nautilus-, Chrome- und Firefox-Integration. Das KDE Connect-Team stellt für Linux, BSD, Android, Sailfish, macOS und Windows Anwendungen bereit." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "Mit GSConnect kƶnnen Sie mobile GerƤte und andere Desktops sicher verbinden mit:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Dateien, Links und Text teilen" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Nachrichten senden und abrufen" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Inhalt der Zwischenablage abgleichen" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Kontakte abgleichen" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Abgleich-Benachrichtigungen" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Medienwiedergaben steuern" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "SystemlautstƤrke steuern" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Vordefinierte Befehle ausführen" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "Und mehr …" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect in der GNOME Shell" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Verbinden mit …" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Abbruch" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Verbinden" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP-Adresse" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "Keine Kontakte" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Hilfe" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Telefonnummer oder Name eingeben" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Andere" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "SMS senden" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Senden" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "GerƤt ist getrennt" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Nachricht senden" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Eine Nachricht verfassen" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "Nachrichteneintrag" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Tippen Sie eine Nachricht, zum Senden die Eingabetaste drücken" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Nachrichten" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Neue Konversation" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Keine Konversationen" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Keine Konversation ausgewƤhlt" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "WƤhle oder starte eine Konversation" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Befehl bearbeiten" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Speichern" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Name" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Befehlszeile" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Ein ausführbares Programm auswƤhlen" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Ɩffnen" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Desktop" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "Kamera" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Zwischenablagen-Abgleich" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Medienwiedergaben" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Maus & Tastatur" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "LautstƤrkeregler" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Dateien" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Dateien empfangen" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Dateien speichern unter" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Teilen" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "GerƤteakku" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Benachrichtigung bei geringem Akkustand" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "Ā»Benutzerdefinierter Ladestand erreichtĀ«-Benachrichtigung" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Ā»VollstƤndig geladenĀ«-Benachrichtigung" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Systemakku" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Statistik teilen" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Akku" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Befehle" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Befehl hinzufügen" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Benachrichtigungen freigeben" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Teilen wenn aktiv" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Anwendungen" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Benachrichtigungen" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Kontakte" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Eingehende Anrufe" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "LautstƤrke" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Medienwiedergabe pausieren" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Laufende Anrufe" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Mikrofon stummschalten" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Telefonie" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Aktionstastenkürzel" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Alles zurücksetzen …" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Tastenkürzel" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Module" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Experimentell" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "GerƤtezwischenspeicher" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Zwischenspeicher leeren …" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Alte SMS-Unterstützung" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "Automatisches SFTP-EinhƤngen" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "Erweitert" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Tastenkürzel" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "GerƤteeinstellungen" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Koppeln" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "GerƤt ist nicht gekoppelt" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Sie kƶnnen dieses GerƤt vor dem Koppeln konfigurieren" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Verschlüsselungsinfo" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Entkoppeln" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Zum GerƤt" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Vom GerƤt" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Nichts" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Wiederherstellen" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Leiser" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Stummschalten" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Festlegen" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Zum Abbrechen ESC oder die Rücktaste drücken, um das Tastenkürzel zurückzusetzen." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "GerƤtename" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_Umbenennen" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Auffrischen" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "Handy-Einstellungen" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Service-Menü" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "GerƤtemenü" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "GerƤtenamen bearbeiten" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "GerƤte" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "GerƤte werden gesucht…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Browser-Erweiterungen" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Aktivieren" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Dieses GerƤt ist für nicht gekoppelte GerƤte unsichtbar" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Erkennen deaktiviert" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Anzeigemodus" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Leiste" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "Benutzermenü" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Hilfeprotokoll generieren" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "Über GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "GerƤt auswƤhlen" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "AuswƤhlen" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Kein GerƤt gefunden" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "GerƤteliste" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Bericht" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Etwas ist schiefgelaufen" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "GSConnect hatte ein unerwartetes Problem. Bitte melden Sie den Fehler und stellen Sie nützliche Informationen bereit." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Technische Details" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "An MobilgerƤt senden" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Mobile GerƤte" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Aktivieren" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d verbunden" msgstr[1] "%d verbunden" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Deaktivieren" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Bearbeiten" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Entfernen" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Deaktiviert" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Ein neues Tastenkürzel eingeben, um %s zu Ƥndern" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s wird bereits verwendet" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Eine vollstƤndige KDE-Connect-Implementierung für GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "taaem \n" "Tobias Bannert \n" "Bjƶrn Daase (BjoernDaase)" #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "Debug-Meldungen werden protokolliert. Führen Sie alle erforderlichen Schritte aus, um ein Problem zu reproduzieren und überprüfen Sie dann das Protokoll." #: src/preferences/service.js:414 msgid "Review Log" msgstr "Protokoll überprüfen" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Laptop" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Smartphone" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tablet" #: src/preferences/service.js:488 msgid "Television" msgstr "Fernseher" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Ungekoppelt" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Getrennt" #: src/preferences/service.js:518 msgid "Connected" msgstr "Verbunden" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "Auf Dienst wird gewartet …" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "Für Hilfe bei der Fehlerbehebung hier klicken" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "Für mehr Informationen klicken" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Nummer wƤhlen" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Datei freigeben" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Alle verfügbaren GerƤte anzeigen" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Alle GerƤte anzeigen" #: src/service/daemon.js:369 msgid "Target Device" msgstr "ZielgerƤt" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Nachrichtentext" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Benachrichtigung senden" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "Name der Benachrichtigungs-App" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Benachrichtigungsinhalt" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Benachrichtigungssymbol" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "Benachrichtigungs-ID" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Foto" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Klingeln" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Link freigeben" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Text teilen" #: src/service/daemon.js:528 msgid "Show release version" msgstr "Neuste Version zeigen" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Nicht verfügbar" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Bluetooth-GerƤt bei %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "%s Fingerabdruck:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Kopplung von %s angefordert" #: src/service/device.js:800 msgid "Reject" msgstr "Ablehnen" #: src/service/device.js:805 msgid "Accept" msgstr "Annehmen" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "Erkennen wurde aufgrund der Anzahl an GerƤten in diesem Netzwerk deaktiviert." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSL nicht gefunden" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "Port wird bereits verwendet" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "Akku-Informationen austauschen" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: Akku ist voll" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "VollstƤndig geladen" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "%s: Akku hat beutzerdefinierten Ladestand erreicht" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "%d%% geladen" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: Akku ist leer" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% verbleibend" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Zwischenablage" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "Inhalt der Zwischenablage freigeben" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "Zwischenablage senden" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "Zwischenablage laden" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "Auf Kontakte des verbundenen GerƤtes zugreifen" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Mein Handy finden" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "Das verbundene GerƤt klingeln lassen" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "Mauspad" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "Das verbundene GerƤt als entfernte Maus und Tastatur verwenden" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Tastatur" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "Bidirektionale Steuerung der Medienwiedergabe" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "Unbekannt" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "Benachrichtigungen für das verbundene GerƤt freigeben" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Benachrichtigung abbrechen" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Benachrichtigung schließen" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "Auf Benachrichtigung antworten" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Benachrichtigung aktivieren" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "Das verbundene GerƤt anweisen, ein Foto schießen und es an diesen PC zu übertragen" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Übertragung gescheitert" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Senden von Ā»%sĀ« an %s gescheitert" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "Pings senden und empfangen" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "PrƤsentation" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "Das verbundene GerƤt als PrƤsentations-Fernbedienung nutzen" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Befehle ausführen" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "Befehle auf dem verbundenen GerƤt ausführen oder das GerƤt vordefinierte Befehle auf diesem Rechner ausführen lassen" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "Das Dateisystem des verbundenen GerƤtes durchsuchen" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "EinhƤngen" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "AushƤngen" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s meldete einen Fehler" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Teilen" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "Dateien und URLs zwischen GerƤten freigeben" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s besitzt keine Berechtigung, Dateien hochzuladen" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "Übertrage Datei" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "Ā»%sĀ« wird von %s empfangen" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Übertragung erfolgreich" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "Ā»%sĀ« von %s empfangen" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Ordner ƶffnen" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Datei ƶffnen" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Empfang von ā€ž%sā€œ von %s gescheitert" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Text von %s freigegeben" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Ā»%sĀ« wird an %s gesendet" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "Ā»%sĀ« an %s gesendet" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Dateien an %s senden" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Ɩffnen wenn abgeschlossen" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Link an %s senden" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "SMS über das verbundene GerƤt senden/empfangen und über neue SMS benachrichtigt werden" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Neue SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "Auf SMS antworten" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "SMS freigeben" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "SystemlautstƤrke" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "Dem verbundenen GerƤt die Steuerung der SystemlautstƤrke erlauben" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio nicht gefunden" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "Über Anrufe benachrichtigt werden und SystemlautstƤrke wƤhrend klingelnden/laufenden Anrufen anpassen" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Anruf stummschalten" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Unbekannter Kontakt" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Eingehender Anruf" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Laufender Anruf" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Fax" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "GeschƤftlich" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Mobil" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Privat" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "An %s senden" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Gerade jetzt" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Gestern惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d Minute" msgstr[1] "%d Minuten" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Gruppennachricht" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Sie: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "Und %d anderer Kontakt" msgstr[1] "Und %d andere" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "Tastaturfernsteuerung auf %s ist nicht aktiv" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (wird geschƤtzt …)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d:%02d bis vollstƤndig aufgeladen)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d∶%02d verbleibend)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Antworten" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Verweise mit GSConnect direkt an den Browser oder per SMS freigeben." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Dienst nicht verfügbar" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Im Browser ƶffnen" gnome-shell-extension-gsconnect-50/po/es.po000066400000000000000000001061621421543444100211020ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2022-02-28 08:39\n" "Last-Translator: \n" "Language-Team: Spanish\n" "Language: es_ES\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: es-ES\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "Una implementación de KDE Connect para GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "Equipo de GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnect es una implementación completa de KDE Connect especialmente para GNOME Shell con integración para Nautilus, Chrome y Firefox. El equipo de KDE Connect tiene aplicaciones para Linux, BSD, Android, Sailfish, macOS y Windows." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "Con GSConnect, puede conectar de manera segura con dispositivos móviles y de escritorio para:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Compartir archivos, enlaces y texto" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Enviar y recibir mensajes" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Sincronizar el contenido del portapapeles" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Sincronizar los contactos" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Sincronizar las notificaciones" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Controlar reproductores multimedia" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "Controlar el volumen del sistema" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Ejecutar órdenes predefinidas" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "Y mĆ”s…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect en GNOME Shell" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Conectar a…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Cancelar" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Conectar" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "Dirección IP" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "No hay ningĆŗn contacto" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Ayuda" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Escriba un nĆŗmero telefónico o un nombre" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Otro" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "Enviar SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Enviar" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "El dispositivo estĆ” desconectado" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Enviar mensaje" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Escriba un mensaje" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "Entrada de mensaje" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Escriba un mensaje y presione Enter para enviar" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "MensajerĆ­a" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Conversación nueva" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "No hay ninguna conversación" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "No se seleccionó ninguna conversación" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Seleccione una conversación o inicie una" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Editar orden" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Guardar" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Nombre" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "LĆ­nea de órdenes" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Elija un ejecutable" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Abrir" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Equipo de escritorio" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "CĆ”mara" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Sincronización de portapapeles" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Reproductores multimedia" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Ratón y teclado" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Control de volumen" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Archivos" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Recibir archivos" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Guardar archivos en" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Compartición" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "BaterĆ­a del dispositivo" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Notificación de baterĆ­a baja" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "Notificación de nivel de carga personalizado" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Notificación de carga completa" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "BaterĆ­a del sistema" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Compartir estadĆ­sticas" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "BaterĆ­a" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Ɠrdenes" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "AƱadir orden" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Notificaciones de compartición" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Compartir mientras haya actividad" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Aplicaciones" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Notificaciones" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Contactos" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Llamadas entrantes" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Volumen" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Pausar multimedia" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Llamadas en curso" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Silenciar micrófono" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "TelefonĆ­a" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Atajos de acciones" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Restablecer todo…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Atajos" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Complementos" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Experimentos" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Antememoria de dispositivo" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Vaciar antememoria…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Compatibilidad SMS heredada" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "Montaje automĆ”tico SFTP" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "Avanzado" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Atajos de teclado" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "Configuración del dispositivo" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Emparejamiento" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "El dispositivo no estĆ” emparejado" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Puede configurar este dispositivo antes de emparejarlo" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Información de cifrado" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Desemparejar" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Al dispositivo" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Del dispositivo" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Nada" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Restaurar" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Disminuir" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Silenciar" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Establecer" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Oprima Esc para cancelar o Retroceso para restablecer el atajo de teclado." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Nombre del dispositivo" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_Cambiar nombre" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Actualizar" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "Configuración de móvil" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "MenĆŗ de servicios" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "MenĆŗ de dispositivos" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Editar nombre de dispositivo" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Dispositivos" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Buscando dispositivos…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Complementos para navegadores" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Activar" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Este dispositivo es invisible a dispositivos no emparejados" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Descubrimiento desactivado" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Modo de visualización" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Panel" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "MenĆŗ de usuario" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Generar registro para asistencia" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "Acerca de GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Seleccione un dispositivo" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Seleccionar" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "No se encontró ningĆŗn dispositivo" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Lista de dispositivos" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Informe" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Algo ha salido mal" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "GSConnect encontró un error inesperado. Por favor, informe del problema e incluya cualquier información que pueda ayudar." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Detalles tĆ©cnicos" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "Enviar a dispositivo móvil" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Dispositivos móviles" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Encender" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d conectado" msgstr[1] "%d conectados" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Desactivar" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Editar" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Quitar" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Desactivado" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Digite un atajo nuevo para cambiar %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "Ya estĆ” utilizĆ”ndose %s" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Una completa implementación de KDE Connect para GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "Adolfo Jayme Barrientos , 2018-2019" #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "Los mensajes de depuración estĆ”n registrĆ”ndose. Realice las acciones necesarias para reproducir un problema y, a continuación, revise el registro." #: src/preferences/service.js:414 msgid "Review Log" msgstr "Revisar registro" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Equipo portĆ”til" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "TelĆ©fono inteligente" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tableta" #: src/preferences/service.js:488 msgid "Television" msgstr "Televisión" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Desemparejado" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Desconectado" #: src/preferences/service.js:518 msgid "Connected" msgstr "Conectado" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "Esperando el servicio…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "Pulse para obtener información de solución de problemas" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "Pulse para mĆ”s información" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Marcar nĆŗmero" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Compartir archivo" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Enumerar dispositivos disponibles" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Enumerar todos los dispositivos" #: src/service/daemon.js:369 msgid "Target Device" msgstr "Dispositivo de destino" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Cuerpo del mensaje" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Enviar notificación" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "Nombre de la aplicación de la notificación" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Cuerpo de la notificación" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Icono de la notificación" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "Identificador de la notificación" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "FotografĆ­a" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Prueba de conectividad" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Timbrar" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Compartir enlace" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Compartir texto" #: src/service/daemon.js:528 msgid "Show release version" msgstr "Mostrar versión" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "No disponible" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Dispositivo Bluetooth en %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "Huella digital de %s:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Solicitud de emparejamiento de %s" #: src/service/device.js:800 msgid "Reject" msgstr "Rechazar" #: src/service/device.js:805 msgid "Accept" msgstr "Aceptar" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "Se desactivó el descubrimiento debido al nĆŗmero de dispositivos presentes en esta red." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "No se encontró OpenSSL" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "Puerto ya en uso" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "Intercambiar información sobre la baterĆ­a" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: baterĆ­a cargada" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "Carga completa" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "%s: baterĆ­a cargada al nivel personalizado" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "%dĀ %% cargada" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: baterĆ­a baja" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%dĀ %% restante" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Portapapeles" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "Compartir el contenido del portapapeles" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "EnvĆ­o a portapapeles" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "Recepción desde portapapeles" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "Acceder a los contactos del dispositivo emparejado" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Encontrar mi telĆ©fono" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "Hacer sonar su dispositivo emparejado" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "Mousepad" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "Permite que el dispositivo emparejado actĆŗe como ratón y teclado remotos" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Teclado" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "Control remoto de reproducción multimedia bidireccional" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "Desconocido" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "Compartir notificaciones con el dispositivo emparejado" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Cancelar notificación" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Cerrar notificación" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "Notificación de respuesta" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Activar notificación" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "Solicitar al dispositivo emparejado que tome una foto y la transfiera a este equipo" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Transferencia fallida" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Falló el envĆ­o de Ā«%sĀ» a %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "Enviar y recibir pings" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Prueba de conectividad: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "Presentación" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "Utilizar el dispositivo emparejado como presentador" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Ejecutar órdenes" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "Ejecute órdenes en su dispositivo emparejado o deje que el dispositivo ejecute órdenes predefinidas en este equipo" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "Examinar el sistema de archivos del dispositivo emparejado" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Montar" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Desmontar" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s informó de un error" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Compartición" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "Compartir archivos y URLs entre dispositivos" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s no tiene permitido cargar archivos" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "Transfiriendo archivo" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "Recibiendo Ā«%sĀ» de %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Transferencia exitosa" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "Se recibió Ā«%sĀ» de %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Abrir carpeta" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Abrir archivo" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Falló la recepción de Ā«%sĀ» desde %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Texto compartido por %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Enviando Ā«%sĀ» a %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "Se envió Ā«%sĀ» a %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Enviar archivos a %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Abrir al terminar" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Enviar un enlace a %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "Enviar y leer SMS del dispositivo emparejado y recibir notificaciones de nuevos SMS" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "SMS nuevo (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "Responder a SMS" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "Compartir SMS" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Volumen del sistema" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "Habilitar el dispositivo emparejado para controlar el volumen del sistema" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "No se encontró PulseAudio" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "Ser notificado sobre las llamadas y ajustar el volumen del sistema durante las llamadas que suenan/estĆ”n en curso" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Silenciar llamada" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Contacto desconocido" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Llamada entrante" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Llamada en curso" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Fax" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Trabajo" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Móvil" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Residencial" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Enviar a %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Ahora mismo" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Ayer惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minuto" msgstr[1] "%d minutos" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Mensaje grupal" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Usted: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "Y %d contacto mĆ”s" msgstr[1] "Y %d mĆ”s" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "El teclado remoto de %s no estĆ” activo" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%dĀ %% (estimando…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%dĀ %% (%d∶%02d hasta completarse)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%dĀ %% (quedan %d∶%02d)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Responder" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Comparta enlaces con GSConnect, directamente al navegador o a travĆ©s de SMS." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Servicio no disponible" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Abrir en el navegador" gnome-shell-extension-gsconnect-50/po/et.po000066400000000000000000001035661421543444100211100ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-12-03 16:29\n" "Last-Translator: \n" "Language-Team: Estonian\n" "Language: et_EE\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: et\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "KDE Connecti teostus GNOMEle" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "GSConnecti meeskond" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnect on tƤielik KDE Connecti teostus eelkƵige GNOME Shellile koos Nautiluse, Chrome'i ja Firefoxi integratsiooniga. KDE Connecti meeskonnal on rakendused Linuxi, BSD, Androidi, Sailfishi, macOSi ja Windowsi jaoks." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "GSConnecti abil saad sa turvaliselt ühenduda mobiilseadmete ja teiste tƶƶlaudadega, et:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Jagada faile, linke ja teksti" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Saata ja vastu vƵtta sƵnumeid" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Sünkroonida lƵikelaua sisu" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Sünkroonida kontakte" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Sünkroonida teatisi" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Juhtida meediamƤngijaid" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "Juhtida süsteemi helitugevust" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "KƤivitada eelmƤƤratud kƤsklusi" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "Ja muud…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect GNOME Shellis" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Ühendu…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Loobu" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Ühenda" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP-aadress" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "Kontakte pole" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Abi" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Kirjuta telefoninumber vƵi nimi" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Muu" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "Saada SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Saada" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Seade on lahti ühendatud" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Saada sƵnum" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Kirjuta sƵnum" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "SƵnumi sisestamine" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Kirjuta sƵnum ja vajuta saatmiseks Enter" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "SƵnumside" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Uus vestlus" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Vestlused puuduvad" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Ühtegi vestlust pole valitud" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Vali vƵi alusta vestlus" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Muuda kƤsklust" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Salvesta" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Nimi" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "KƤsurida" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Vali kƤivitatav" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Ava" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Lauaarvuti" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "Kaamera" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "LƵikelaua sünkroonimine" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "MeediamƤngijad" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Hiir ja klaviatuur" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Helitugevuse juhtimine" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Failid" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "VƵta faile vastu" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Salvesta failid asukohta" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Jagamine" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Seadme aku" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Tühjeneva aku teade" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "Teade kohandatud taseme laadimisest" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "TƤislaetud aku teade" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Süsteemi aku" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Jaga statistikat" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Aku" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "KƤsklused" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Lisa kƤsklus" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Jaga teateid" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Jaga, kui on aktiivne" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Rakendused" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Teated" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Kontaktid" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Sissetulevad kƵned" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Helitugevus" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Peata meedia" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "KƤimasolevad kƵned" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Vaigista mikrofon" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Telefonifunktsioon" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Tegevuste otseteed" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "LƤhtesta kƵik…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Otseteed" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Pluginad" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Katseline" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Seadme vahemƤlu" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Tühjenda vahemƤlu…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "PƤrand SMS-tugi" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "SFTP automaathaakimine" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "TƤpsemad" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Klaviatuuriotseteed" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "Seadme seaded" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Paarita" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "Seade on paaritamata" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Sa vƵid seda seadet enne paaritamist seadistada" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Krüpteeringu teave" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Eemalda paardumine" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Seadmesse" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Seadmest" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Puudub" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Taasta" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Vaiksem" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Vaigista" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "MƤƤra" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Vajuta tühistamiseks Esc vƵi klaviatuuriotsetee lƤhtestamiseks Tagasilüke." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Seadme nimi" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "Nimeta ümber" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "VƤrskenda" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "Mobiiliseaded" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Teenusemenüü" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "Seadmemenüü" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Muuda seadme nime" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Seadmed" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Seadmete otsimine…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Brauserilaiendused" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Luba" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "See seade on paardumata seadmetele nƤhtamatu" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Avastamine keelatud" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Ekraanirežiim" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Paneel" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "Kasutajamenüü" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Genereeri tugiteenusele logi" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "GSConnecti teave" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Vali seade" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Vali" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Seadet ei leitud" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Seadmete loend" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Teata" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Midagi lƤks valesti" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "GSConnectil esines ootamatu viga. Palun teata veast ja lisa mistahes infot, mis vƵib lahendamisel kaasa aidata." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Tehnilised andmed" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "Saada mobiilseadmesse" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Mobiilseadmed" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Lülita sisse" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d ühendatud" msgstr[1] "%d ühendatud" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Lülita vƤlja" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Muuda" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Eemalda" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Keelatud" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Sisesta %s muutmiseks uus otsetee" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s on juba kasutusel" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "TƤielik KDE Connect'i teostus GNOMEle" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "Madis O, 2018." #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "SilumissƵnumid logitakse. Teosta mistahes vajalikud sammud probleemi taasloomiseks, seejƤrel vaata logi üle." #: src/preferences/service.js:414 msgid "Review Log" msgstr "Vaata logi üle" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Sülearvuti" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Nutitelefon" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tahvelarvuti" #: src/preferences/service.js:488 msgid "Television" msgstr "Televisioon" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Paaritamata" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Ühendus katkestatud" #: src/preferences/service.js:518 msgid "Connected" msgstr "Ühendatud" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "Teenuse ootamine…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "KlƵpsa, et saada veaotsingul abi" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "KlƵpsa rohkema teabe saamiseks" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Helista telefoninumbrile" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Jaga faili" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Kuva saadaolevad seadmed" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Kuva kƵik seadmed" #: src/service/daemon.js:369 msgid "Target Device" msgstr "Sihtseade" #: src/service/daemon.js:411 msgid "Message Body" msgstr "SƵnumi sisu" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Saada teade" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "Teavitava rakenduse nimi" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Teate sisu" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Teate ikoon" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "Teate ID" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Foto" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Helise" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Jaga linki" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Jaga teksti" #: src/service/daemon.js:528 msgid "Show release version" msgstr "Kuva vƤljalaskeversiooni" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Pole saadaval" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Bluetooth-seade aadressil %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "%s sƵrmejƤlg:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Paaritamistaotlus seadmelt %s" #: src/service/device.js:800 msgid "Reject" msgstr "Keeldu" #: src/service/device.js:805 msgid "Accept" msgstr "NƵustu" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "Avastamine on keelatud selles vƵrgus olevate seadmete arvu tƵttu." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSLi ei leitud" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "Port on juba kasutusel" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "Aku infovahetus" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: aku on tƤis" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "TƤielikult laetud" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "%s: Aku on laetud kohandatud tasemeni" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "%d%% Laetud" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: aku on tühi" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% jƤƤnud" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "LƵikelaud" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "Jaga lƵikelaua sisu" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "LƵikelaua saatmine" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "LƵikelaua vastuvƵtmine" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "LigipƤƤs ühendatud seadme kontaktide juurde" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Leia mu telefon" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "Helista oma seotud seadmele" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "Hiirepadi" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "VƵimaldab seotud seadmel tƶƶtada hiire ja klaviatuurina" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Klaviatuur" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "Kahesuunaline meediumi kaugjuhtimine" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "Teadmata" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "Jaga teavitusi seotud seadmega" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Tühista teade" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Sulge teade" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "Vasta teatele" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Aktiveeri teade" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "Tee seotud seadmega pilti ning saada see arvutisse" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Ülekanne ebaƵnnestus" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "ā€ž%sā€œsaatmine seadmesse %s ebaƵnnestus" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "Pingide saatmine ja vastu vƵtmine" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "Esitlus" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "Kasuta seotud seadet presentatsiooniks" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "KƤivita kƤsklusi" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "KƤivita kƤsklusi seotud seadmel vƵi luba seadmel kƤivitada ettemƤƤratuid kƤske selles arvutis" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "Sirvi seotud seadme failisüsteemi" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Haagi" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Haagi lahti" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s teatas vea" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Jaga" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "URL-i ja failide jagamine seadmete vahel" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s ei tohi faile üles laadida" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "Faili edastamine" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "ā€ž%sā€œvastuvƵtmine seadmelt %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Ülekanne edukas" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "ā€ž%sā€œvastu vƵetud seadmelt %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Ava kataloog" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Ava fail" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "ā€ž%sā€œvastuvƵtmine seadmest %s ebaƵnnestus" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "%s poolt jagatud tekst" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Saadan ā€ž%sā€œseadmesse %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "ā€ž%sā€œsaadetud seadmesse %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Saada faile seadmesse %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Ava, kui valmis" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Saada link seadmesse %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "Saada ja loe seotud seadme SMS-e ning saa teadet uute SMS-ide kohta" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Uus SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "Vasta SMSile" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "Jaga SMSi" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Süsteemi helitugevus" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "Luba ühendatud seadmel juhtida süsteemi helitugevust" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudiot ei leitud" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "Saa teadet kƵnede kohta ning reguleeri saabuvate/vƤljuvate kƵnede helitugevust" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Vaigista kƵne" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Tundmatu kontakt" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Sissetulev kƵne" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "VƤljuv kƵne" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Faks" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Tƶƶ" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Mobiil" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Kodu" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Saada seadmesse %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Praegu" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Eile惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minut" msgstr[1] "%d minutit" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "GrupisƵnum" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Sina: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "Ja %d teine kontakt" msgstr[1] "Ja %d teist" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "Kaugklaviatuur ei ole seadmes %s aktiivne" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (hindamine…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (tƤitumiseni %d∶%02d)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (jƤƤnud %d∶%02d)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Vasta" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Jaga linke GSConnectiga, otse brauserisse vƵi SMSi teel." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Teenus pole saadaval" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Ava brauseris" gnome-shell-extension-gsconnect-50/po/fa-IR.po000066400000000000000000001114761421543444100213750ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-09 01:50\n" "Last-Translator: \n" "Language-Team: Persian\n" "Language: fa_IR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: fa\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "Ų¬ŪŒā€ŒŲ§Ų³ā€ŒŚ©Ų§Ł†Ś©ŲŖ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "Ł¾ŪŒŲ§ŲÆŁ‡ā€ŒŲ³Ų§Ų²ŪŒ Ś©ŪŒā€ŒŲÆŪŒā€ŒŲ§ŪŒ کانکت برای ŚÆŁ†ŁˆŁ…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "ŚÆŲ±ŁˆŁ‡ Ų¬ŪŒā€ŒŲ§Ų³ā€ŒŚ©Ų§Ł†Ś©ŲŖ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "Ų¬ŪŒā€ŒŲ§Ų³ā€ŒŚ©Ų§Ł†Ś©ŲŖŲŒ Ł¾ŪŒŲ§ŲÆŁ‡ā€ŒŲ³Ų§Ų²ŪŒ Ś©Ų§Ł…Ł„ŪŒ Ų§Ų² Ś©ŪŒā€ŒŲÆŪŒā€ŒŲ§ŪŒ Ś©Ų§Ł†Ś©ŲŖŲŒ Ł…Ų®ŲµŁˆŲµ Ł¾ŁˆŲ³ŲŖŁ‡Ł” ŚÆŁ†ŁˆŁ… ŲØŲ§ ŪŒŚ©Ł¾Ų§Ų±Ś†ŚÆŪŒ Ł†Ų§ŲŖŪŒŁ„ŁˆŲ³ŲŒ Ś©Ų±ŁˆŁ… و فایرفاکس Ų§Ų³ŲŖ. ŚÆŲ±ŁˆŁ‡ Ś©ŪŒā€ŒŲÆŪŒā€ŒŲ§ŪŒ Ś©Ų§Ł†Ś©ŲŖŲŒ ŲØŲ±Ł†Ų§Ł…Ł‡ā€ŒŁ‡Ų§ŪŒŪŒ برای ŚÆŁ†Łˆ/Ł„ŪŒŁ†ŁˆŚ©Ų³ŲŒ ŲØŪŒā€ŒŲ§Ų³ā€ŒŲÆŪŒŲŒ Ų§Ł†ŲÆŲ±ŁˆŪŒŲÆŲŒ Ų³ŪŒŁ„ā€ŒŁŪŒŲ³ŲŒ Ł…Ś© Ų§Łˆā€ŒŲ§Ų³ و ŁˆŪŒŁ†ŲÆŁˆŲ² دارند." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "ŲØŲ§ Ų¬ŪŒā€ŒŲ§Ų³ā€ŒŚ©Ų§Ł†Ś©ŲŖ Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŪŒŲÆ به صورت امن به Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŁ‡Ų§ŪŒ همراه و دیگر Ł…ŪŒŲ²Ś©Ų§Ų±Ł‡Ų§ŪŒŲŖŲ§Ł† ŁˆŲµŁ„ ؓوید ŲŖŲ§:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Ł¾Ų±ŁˆŁ†ŲÆŁ‡ā€ŒŁ‡Ų§ŲŒ Ł¾ŪŒŁˆŁ†ŲÆŁ‡Ų§ و متن Ų±Ų§ Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ Ś©Ł†ŪŒŲÆ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Ł¾ŪŒŲ§Ł…ā€ŒŁ‡Ų§ Ų±Ų§ فرستاده و دریافت Ś©Ł†ŪŒŲÆ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Ł…Ų­ŲŖŁˆŲ§ŪŒ ŲŖŲ®ŲŖŁ‡ā€ŒŚÆŪŒŲ±Ł‡ Ų±Ų§ همگام Ś©Ł†ŪŒŲÆ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Ų¢Ų“Ł†Ų§ŪŒŲ§Ł† Ų±Ų§ همگام Ś©Ł†ŪŒŲÆ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Ų¢ŚÆŲ§Ł‡ŪŒā€ŒŁ‡Ų§ Ų±Ų§ همگام Ś©Ł†ŪŒŲÆ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Ł¾Ų®Ų“ā€ŒŚ©Ł†Ł†ŲÆŁ‡ā€ŒŁ‡Ų§ŪŒ رسانه Ų±Ų§ وابپایید" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "حجم صدای سامانه Ų±Ų§ وابپایید" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "ŲÆŲ³ŲŖŁˆŲ±Ł‡Ų§ŪŒ Ų§Ų² پیؓ ŲŖŲ¹Ų±ŪŒŁā€ŒŲ“ŲÆŁ‡ Ų±Ų§ Ų§Ų¬Ų±Ų§ Ś©Ł†ŪŒŲÆ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "و ŲØŪŒŲ“ā€ŒŲŖŲ±ā€¦" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "Ų¬ŪŒā€ŒŲ§Ų³ā€ŒŚ©Ų§Ł†Ś©ŲŖ ŲÆŲ± Ł¾ŁˆŲ³ŲŖŁ‡Ł” ŚÆŁ†ŁˆŁ…" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "ŁˆŲµŁ„ ؓدن به…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Ł„ŲŗŁˆ" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "ŁˆŲµŁ„ ؓدن" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "Ł†Ų“Ų§Ł†ŪŒ Ų¢ŪŒā€ŒŁ¾ŪŒ" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "ŲØŲÆŁˆŁ† آؓنا" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "راهنما" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "ؓماره تلفن یا Ł†Ų§Ł…ŪŒ Ų±Ų§ ŲØŪŒŲ§Ų²Ł…Ų§ŪŒŪŒŲÆ" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "دیگر" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "فرستادن Ł¾ŪŒŲ§Ł…Ś©" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "فرستادن" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "افزاره قطع Ų§Ų³ŲŖ" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "فرستادن Ł¾ŪŒŲ§Ł…" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Ł†ŁˆŲ“ŲŖŁ† یک Ł¾ŪŒŲ§Ł…" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "ورودی Ł¾ŪŒŲ§Ł…" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Ł¾ŪŒŲ§Ł…ŪŒ Ł†ŁˆŲ“ŲŖŁ‡ و برای ŁŲ±Ų³ŲŖŲ§ŲÆŁ†ŲŒ ورود Ų±Ų§ ŲØŲ²Ł†ŪŒŲÆ" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Ł¾ŪŒŲ§Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "ŚÆŁŲŖā€ŒŁˆŚÆŁˆŪŒ جدید" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "ŲØŲÆŁˆŁ† ŚÆŁŲŖā€ŒŁˆŚÆŁˆ" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "ŚÆŁŲŖā€ŒŁˆŚÆŁˆŪŒ ŚÆŲ²ŪŒŲÆŁ‡ نؓده" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "ŚÆŁŲŖā€ŒŁˆŚÆŁˆŪŒŪŒ Ų±Ų§ ŲØŲ±ŚÆŪŒŲÆŁ‡ یا Ų¢ŲŗŲ§Ų² Ś©Ł†ŪŒŲÆ" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "ویرایؓ دستور" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Ų°Ų®ŪŒŲ±Ł‡" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "نام" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Ų®Ų· فرمان" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Ł¾Ų±ŁˆŁ†ŲÆŁ‡Ł” Ų§Ų¬Ų±Ų§ŪŒŪŒā€ŒŲ§ŪŒ ŲØŲ±ŚÆŲ²ŪŒŁ†ŪŒŲÆ" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "ŚÆŲ“ŁˆŲÆŁ†" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Ł…ŪŒŲ²Ś©Ų§Ų±" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "ŲÆŁˆŲ±ŲØŪŒŁ†" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Ł‡Ł…ŚÆŲ§Ł…ā€ŒŲ³Ų§Ų²ŪŒ ŲŖŲ®ŲŖŁ‡ā€ŒŚÆŪŒŲ±Ł‡" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Ł¾Ų®Ų“ā€ŒŚ©Ł†Ł†ŲÆŁ‡ā€ŒŁ‡Ų§ŪŒ رسانه" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Ł…ŁˆŲ“ŪŒ و ŲµŁŲ­Ł‡ā€ŒŚ©Ł„ŪŒŲÆ" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "واپایؓ حجم ŲµŲÆŲ§" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Ł¾Ų±ŁˆŁ†ŲÆŁ‡ā€ŒŁ‡Ų§" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "دریافت Ł¾Ų±ŁˆŁ†ŲÆŁ‡ā€ŒŁ‡Ų§" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Ų°Ų®ŪŒŲ±Ł‡Ł” Ł¾Ų±ŁˆŁ†ŲÆŁ‡ā€ŒŁ‡Ų§ ŲÆŲ±" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "باتری افزاره" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Ų¢ŚÆŲ§Ł‡ŪŒ کم ŲØŁˆŲÆŁ† باتری" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "ŲŖŲ§ Ų¢ŚÆŲ§Ł‡ŪŒ Ų³Ų·Ų­ سفارؓی ؓارژ Ł…ŪŒā€ŒŲ“ŁˆŲÆ" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Ų¢ŚÆŲ§Ł‡ŪŒ پر ؓدن کامل" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "باتری سامانه" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ آمار" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "باتری" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "ŲÆŲ³ŲŖŁˆŲ±Ł‡Ų§" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Ų§ŁŲ²ŁˆŲÆŁ† دستور" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ Ų¢ŚÆŲ§Ł‡ŪŒā€ŒŁ‡Ų§" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ هنگام فعّال ŲØŁˆŲÆŁ† ŲÆŲ± نؓست" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "ŲØŲ±Ł†Ų§Ł…Ł‡ā€ŒŁ‡Ų§" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Ų¢ŚÆŲ§Ł‡ŪŒā€ŒŁ‡Ų§" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Ų¢Ų“Ł†Ų§ŪŒŲ§Ł†" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "ŲŖŁ…Ų§Ų³ā€ŒŁ‡Ų§ŪŒ دریافتی" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "حجم ŲµŲÆŲ§" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "مکث رسانه" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "ŲŖŁ…Ų§Ų³ā€ŒŁ‡Ų§ŪŒ ŲÆŲ± Ų­Ų§Ł„ انجام" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Ų®Ł…ŁˆŲ“ŪŒ Ł…ŪŒŚ©Ų±ŁˆŁŁˆŁ†" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "تلفن" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Ų§ŁŲ²ŁˆŲÆŁ† Ł…ŪŒŲ§Ł†ā€ŒŲØŲ±" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "ŲØŲ§Ų²Ł†Ų“Ų§Ł†ŪŒ همه…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Ł…ŪŒŲ§Ł†ā€ŒŲØŲ±Ł‡Ų§" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Ų§ŁŲ²Ų§ŪŒŁ‡ā€ŒŁ‡Ų§" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Ų¢Ų²Ł…Ų§ŪŒŲ“ŪŒ" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "انبارهٔ افزاره" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Ł¾Ų§Ś©ā€ŒŲ³Ų§Ų²ŪŒ انباره…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Ł¾Ų“ŲŖŪŒŲ§ŲØŁ† Ł¾ŪŒŲ§Ł…Ś© Ł‚ŲÆŪŒŁ…ŪŒ" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "سوار کردن خودکار SFTP" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "Ł¾ŪŒŲ“ā€ŒŲ±ŁŲŖŁ‡" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Ł…ŪŒŲ§Ł†ā€ŒŲØŲ±Ł‡Ų§ŪŒ ŲµŁŲ­Ł‡ā€ŒŚ©Ł„ŪŒŲÆ" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ افزاره" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "جفت کردن" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "افزاره Ų¬ŲÆŲ§ ؓده" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "ممکن Ų§Ų³ŲŖ ŲØŲ®ŁˆŲ§Ł‡ŪŒŲÆ پیؓ Ų§Ų² جفت Ś©Ų±ŲÆŁ†ŲŒ Ų§ŪŒŁ† افزاره Ų±Ų§ Ł¾ŪŒŚ©Ų±ŲØŁ†ŲÆŪŒ Ś©Ł†ŪŒŲÆ" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "اطّلاعات Ų±Ł…Ų²Ł†ŚÆŲ§Ų±ŪŒ" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Ų¬ŲÆŲ§ سازی" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "به افزاره" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Ų§Ų² افزاره" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Ł‡ŪŒŚ†" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "ŲØŲ§Ų²ŚÆŲ±ŲÆŲ§Ł†ŪŒ" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "کم کردن" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Ų®Ł…ŁˆŲ“" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "ŲŖŁ†ŲøŪŒŁ…" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "برای ŲØŲ§Ų²Ł†Ų“Ų§Ł†ŪŒ Ł…ŪŒŲ§Ł†ā€ŒŲØŲ± ŲµŁŲ­Ł‡ā€ŒŚ©Ł„ŪŒŲÆŲŒ گریز یا Ł¾Ų³ā€ŒŲØŲ± Ų±Ų§ ŲØŲ²Ł†ŪŒŲÆ." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "نام افزاره" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_تغییر نام" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Ł†ŁˆŲ³Ų§Ų²ŪŒ" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ تلفن همراه" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "فهرست Ų®ŲÆŁ…ŲŖ" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "فهرست افزاره" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "ویرایؓ نام افزاره" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŁ‡Ų§" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "ŲÆŲ± Ų¬Ų³ŲŖā€ŒŁˆŲ¬ŁˆŪŒ Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŁ‡Ų§ā€¦" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Ų§ŁŲ²ŁˆŁ†Ł‡ā€ŒŁ‡Ų§ŪŒ Ł…Ų±ŁˆŲ±ŚÆŲ±" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "به کار انداختن" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Ų§ŪŒŁ† افزاره برای Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŁ‡Ų§ŪŒ جفت Ł†Ų“ŲÆŁ‡ŲŒ Ł†Ų§Ł…Ų±ŪŒŪŒ Ų§Ų³ŲŖ" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "کؓف Ų§Ų² کار افتاد" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "حالت Ł†Ł…Ų§ŪŒŲ“" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "ŲŖŲ§ŲØŁ„Łˆ" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "فهرست کاربر" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "ایجاد ŚÆŲ²Ų§Ų±Ų“ Ł¾Ų“ŲŖŪŒŲØŲ§Ł†ŪŒ" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "دربارهٔ Ų¬ŪŒā€ŒŲ§Ų³ā€ŒŚ©Ų§Ł†Ś©ŲŖ" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŲ§ŪŒ Ų±Ų§ ŲØŲ±ŚÆŲ²ŪŒŁ†ŪŒŲÆ" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "ŚÆŲ²ŪŒŁ†Ų“" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Ł‡ŪŒŚ† Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŲ§ŪŒ پیدا نؓد" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "فهرست افزاره" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "ŚÆŲ²Ų§Ų±Ų“" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Ś†ŪŒŲ²ŪŒ اؓتباه Ų“ŲÆ" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "Ų¬ŪŒā€ŒŲ§ŪŒā€ŒŚ©Ų§Ł†Ś©ŲŖ ŲØŲ§ خطایی ŲŗŪŒŲ±Ł…Ł†ŲŖŲøŲ±Ł‡ Ų±ŁˆŲØŁ‡ā€ŒŲ±Łˆ Ų“ŲÆ. لطفاً مؓکل Ų±Ų§ ŚÆŲ²Ų§Ų±Ų“ داده و هر Ų§Ų·Ł‘Ł„Ų§Ų¹Ų§ŲŖŪŒ که ممکن Ų§Ų³ŲŖ کمک کند Ų±Ų§ ŲØŲÆŁ‡ŪŒŲÆ." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "جزییات ŁŁ†ŪŒ" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "فرستادن به افزارهٔ همراه" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŁ‡Ų§ŪŒ همراه" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Ų±ŁˆŲ“Ł† کردن" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d ŁˆŲµŁ„ā€ŒŲ“ŲÆŁ‡" msgstr[1] "%d ŁˆŲµŁ„ā€ŒŲ“ŲÆŁ‡" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Ų®Ų§Ł…ŁˆŲ“ کردن" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "ویرایؓ" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "برداؓتن" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Ų§Ų² کار افتاده" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "برای تغییر %s Ł…ŪŒŲ§Ł†ā€ŒŲØŲ± جدیدی وارد Ś©Ł†ŪŒŲÆ" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s Ų§Ų² پیؓ ŲÆŲ± Ų­Ų§Ł„ استفاده Ų§Ų³ŲŖ" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Ł¾ŪŒŲ§ŲÆŁ‡ā€ŒŲ³Ų§Ų²ŪŒ کامل Ś©ŪŒā€ŒŲÆŪŒā€ŒŲ§ŪŒ کانکت برای ŚÆŁ†ŁˆŁ…" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "ŲÆŲ§Ł†ŪŒŲ§Ł„ ŲØŁ‡Ų²Ų§ŲÆŪŒ " #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "Ł¾ŪŒŲ§Ł…ā€ŒŁ‡Ų§ŪŒ رفع اؓکال Ų«ŲØŲŖ Ų“ŲÆŁ‡ā€ŒŲ§Ł†ŲÆ. هر Ų§Ł‚ŲÆŲ§Ł…ŪŒ که برای ŲØŲ§Ų²ŲŖŁˆŁ„ŪŒŲÆ مؓکل لازم Ų§Ų³ŲŖ Ų±Ų§ انجام ŲÆŲ§ŲÆŁ‡ŲŒ سپس ŚÆŲ²Ų§Ų±Ų“ Ų±Ų§ بررسی Ś©Ł†ŪŒŲÆ." #: src/preferences/service.js:414 msgid "Review Log" msgstr "ŲØŲ§Ų²ŲØŪŒŁ†ŪŒ ŚÆŲ²Ų§Ų±Ų“" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Ł„Ł¾ā€ŒŲŖŲ§Ł¾" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "تلفن Ł‡ŁˆŲ“Ł…Ł†ŲÆ" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Ų±Ų§ŪŒŲ§Ł†Ś©" #: src/preferences/service.js:488 msgid "Television" msgstr "ŲŖŁ„ŁˆŪŒŲ²ŪŒŁˆŁ†" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Ų¬ŲÆŲ§ ؓده" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "قطع ؓده" #: src/preferences/service.js:518 msgid "Connected" msgstr "ŁˆŲµŁ„ ؓده" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "ŲÆŲ± انتظار خدمت…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "برای Ų±Ų§Ł‡Ł†Ł…Ų§ŪŒŪŒ برای Ų¹ŪŒŲØā€ŒŪŒŲ§ŲØŪŒ Ś©Ł„ŪŒŚ© Ś©Ł†ŪŒŲÆ" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "برای اطّلاعات بیؓتر Ś©Ł„ŪŒŚ© Ś©Ł†ŪŒŲÆ" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Ų“Ł…Ų§Ų±Ł‡ā€ŒŚÆŪŒŲ±ŪŒ" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "هم Ų±Ų³Ų§Ł†ŪŒ Ł¾Ų±ŁˆŁ†ŲÆŁ‡" #: src/service/daemon.js:351 msgid "List available devices" msgstr "فهرست Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŁ‡Ų§ŪŒ Ł…ŁˆŲ¬ŁˆŲÆ" #: src/service/daemon.js:360 msgid "List all devices" msgstr "فهرست ŲŖŁ…Ų§Ł…ŪŒ Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŁ‡Ų§" #: src/service/daemon.js:369 msgid "Target Device" msgstr "افزارهٔ هدف" #: src/service/daemon.js:411 msgid "Message Body" msgstr "متن Ł¾ŪŒŲ§Ł…" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "فرستادن Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "نام کارهٔ Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "متن Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "نقؓک Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "ؓناسهٔ Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "عکس" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ł¾ŪŒŁ†ŚÆ" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "زنگ" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ Ł¾ŪŒŁˆŁ†ŲÆ" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ متن" #: src/service/daemon.js:528 msgid "Show release version" msgstr "Ł†Ł…Ų§ŪŒŲ“ نگارؓ ارائه" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Ł†Ų§Ł…ŁˆŲ¬ŁˆŲÆ" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "دستگاه ŲØŁ„ŁˆŲŖŁˆŲ« ŲÆŲ± %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "اثرانگؓت %s:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "درخواست جفت کردن Ų§Ų² %s" #: src/service/device.js:800 msgid "Reject" msgstr "Ų±ŲÆ کردن" #: src/service/device.js:805 msgid "Accept" msgstr "پذیرؓ" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "به Ų®Ų§Ų·Ų± ŲŖŲ¹ŲÆŲ§ŲÆ Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŁ‡Ų§ŪŒ روی Ų§ŪŒŁ† Ų“ŲØŚ©Ł‡ŲŒ کؓف Ų§Ų² کار افتاد." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "Ų§ŁˆŁ¾Ł†ā€ŒŲ§Ų³ā€ŒŲ§Ų³ā€ŒŲ§Ł„ پیدا نؓد" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "درگاه Ų§Ų² پیؓ ŲÆŲ± Ų­Ų§Ł„ استفاده Ų§Ų³ŲŖ" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: باتری پر Ų§Ų³ŲŖ" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "ؓارژ کامل Ų“ŲÆ" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "⁦%d٪⁩ ؓارژ Ų“ŲÆ" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: باتری کم Ų§Ų³ŲŖ" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "⁦%d٪⁩ مانده" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "ŲŖŲ®ŲŖŁ‡ā€ŒŚÆŪŒŲ±Ł‡" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "Ł…Ų­ŲŖŁˆŲ§ŪŒ ŲŖŲ®ŲŖŁ‡ā€ŒŚÆŪŒŲ±Ł‡ Ų±Ų§ Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ Ś©Ł†ŪŒŲÆ" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "فرستادن به ŲŖŲ®ŲŖŁ‡ā€ŒŚÆŪŒŲ±Ł‡" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "گرفتن Ų§Ų² ŲŖŲ®ŲŖŁ‡ā€ŒŚÆŪŒŲ±Ł‡" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "به Ł…Ų®Ų§Ų·ŲØŪŒŁ† دستگاه Ų¬ŁŲŖā€ŒŲ³Ų§Ų²ŪŒ ؓده دسترسی پیدا Ś©Ł†ŪŒŲÆ" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "تلفنم Ų±Ų§ بیاب" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "زنگ Ų®ŁˆŲ±ŲÆŁ† دستگاه جفت ؓده Ų“Ł…Ų§" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "صفحهٔ Ł…ŁˆŲ“ŪŒ" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "ŲµŁŲ­Ł‡ā€ŒŚ©Ł„ŪŒŲÆ" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "ناؓناخته" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Ł„ŲŗŁˆ Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "بستن Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "پاسخ به Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "فعال سازی Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Ų¬Ų§ŲØŁ‡ā€ŒŲ¬Ų§ŪŒŪŒ ؓکست خورد" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "ؓکست ŲÆŲ± فرستادن Ā«%sĀ» به %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "Ł¾ŪŒŁ†ŚÆā€ŒŁ‡Ų§ Ų±Ų§ فرستاده و دریافت Ś©Ł†ŪŒŲÆ" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ł¾ŪŒŁ†ŚÆ: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "ارائه" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "اجرای ŲÆŲ³ŲŖŁˆŲ±Ł‡Ų§" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "سوار کردن" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Ł¾ŪŒŲ§ŲÆŁ‡ کردن" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s خطایی ŚÆŲ²Ų§Ų±Ų“ کرد" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ Ł¾Ų±ŁˆŁ†ŲÆŁ‡ā€ŒŁ‡Ų§ و Ł†Ų“Ų§Ł†ŪŒā€ŒŁ‡Ų§ Ł…ŪŒŲ§Ł† ŲÆŲ³ŲŖŚÆŲ§Ł‡ā€ŒŁ‡Ų§" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s اجازهٔ بارگذاری Ł¾Ų±ŁˆŁ†ŲÆŁ‡ā€ŒŁ‡Ų§ Ų±Ų§ ندارد" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "ŲÆŲ± Ų­Ų§Ł„ Ų¬Ų§ŲØŁ‡ā€ŒŲ¬Ų§ŪŒŪŒ Ł¾Ų±ŁˆŁ†ŲÆŁ‡ā€ŒŁ‡Ų§" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "ŲÆŲ± Ų­Ų§Ł„ دریافت Ā«%sĀ» Ų§Ų² %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Ų¬Ų§ŲØŁ‡ā€ŒŲ¬Ų§ŪŒŪŒ Ł…ŁˆŁŁ‚" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "Ā«%sĀ» Ų§Ų² %s دریافت Ų“ŲÆ" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "ŚÆŲ“ŁˆŲÆŁ† ؓاخه" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "ŚÆŲ“ŁˆŲÆŁ† Ł¾Ų±ŁˆŁ†ŲÆŁ‡" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "ؓکست ŲÆŲ± گرفتن Ā«%sĀ» Ų§Ų² %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "متن Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŲÆŁ‡ Ų§Ų² %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "ŲÆŲ± Ų­Ų§Ł„ فرستادن Ā«%sĀ» به %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "Ā«%sĀ» به %s فرستاده Ų“ŲÆ" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "فرستادن Ł¾Ų±ŁˆŁ†ŲÆŁ‡ā€ŒŁ‡Ų§ به %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "ŚÆŲ“ŁˆŲÆŁ† هنگام Ų§ŲŖŁ…Ų§Ł…" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "فرستادن Ł¾ŪŒŁˆŁ†ŲÆŪŒ به %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "Ł¾ŪŒŲ§Ł…Ś©" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Ł¾ŪŒŲ§Ł…Ś© جدید (Ł†Ų“Ų§Ł†ŪŒ)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "پاسخ به Ł¾ŪŒŲ§Ł…Ś©" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ Ł¾ŪŒŲ§Ł…Ś©" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "حجم صدای سامانه" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "Ł¾Ų§Ł„Ų³ā€ŒŲ¢ŲÆŪŒŁˆ پیدا نؓد" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Ų®Ł…ŁˆŲ“ŪŒ ŲŖŁ…Ų§Ų³" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Ų¢Ų“Ł†Ų§ŪŒ ناؓناس" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "ŲŖŁ…Ų§Ų³ دریافتی" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "ŲŖŁ…Ų§Ų³ جاری" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "ŲÆŁˆŲ±Ł†ŚÆŲ§Ų±" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "کاری" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "همراه" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "خانه" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "فرستادن به %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Ł‡Ł…ā€ŒŲ§Ś©Ł†ŁˆŁ†" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "دیروز 惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d ŲÆŁ‚ŪŒŁ‚Ł‡" msgstr[1] "%d ŲÆŁ‚ŪŒŁ‚Ł‡" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Ł¾ŪŒŲ§Ł… ŚÆŲ±ŁˆŁ‡ŪŒ" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Ų“Ł…Ų§: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "و %d Ų¢Ų“Ł†Ų§ŪŒ دیگر" msgstr[1] "و %d Ų¢Ų“Ł†Ų§ŪŒ دیگر" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "ŲµŁŲ­Ł‡ā€ŒŚ©Ł„ŪŒŲÆ دوردست روی %s فعّال Ł†ŪŒŲ³ŲŖ" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "⁦%d٪⁩ (ŲÆŲ± Ų­Ų§Ł„ محاسبه…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "⁦%d٪⁩ (⁦%d:%02d⁩ ŲŖŲ§ پر ؓدن)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "⁦%d٪⁩ (⁦%d:%02d⁩ مانده)" #: src/shell/notification.js:54 msgid "Reply" msgstr "پاسخ" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "ŲØŲ§ Ų¬ŪŒā€ŒŲ§Ų³ Ś©Ų§Ł†Ś©ŲŖŲŒ Ł¾ŪŒŁˆŁ†ŲÆŁ‡Ų§ Ų±Ų§ ŲØŲ§ Ł¾ŪŒŲ§Ł…Ś© یا Ł…Ų³ŲŖŁ‚ŪŒŁ…Ų§Ł‹ ŲÆŲ± Ł…Ų±ŁˆŲ±ŚÆŲ± Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ Ś©Ł†ŪŒŲÆ." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Ų®ŲÆŁ…ŲŖ Ł†Ų§Ł…ŁˆŲ¬ŁˆŲÆ" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "ŚÆŲ“ŁˆŲÆŁ† ŲÆŲ± Ł…Ų±ŁˆŲ±ŚÆŲ±" gnome-shell-extension-gsconnect-50/po/fa.po000066400000000000000000001130421421543444100210540ustar00rootroot00000000000000# Persian translations for org.gnome.Shell.Extensions.GSConnect package. # Copyright (C) 2021 THE org.gnome.Shell.Extensions.GSConnect'S COPYRIGHT HOLDER # This file is distributed under the same license as the org.gnome.Shell.Extensions.GSConnect package. # Automatically generated, 2021. # msgid "" msgstr "" "Project-Id-Version: org.gnome.Shell.Extensions.GSConnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-10-04 17:41+0330\n" "Last-Translator: eshagh \n" "Language-Team: Persian\n" "Language: fa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n==0 || n==1);\n" "X-Generator: Poedit 3.0\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "Ų¬ŪŒā€ŒŲ§Ų³ā€ŒŚ©Ų§Ł†Ś©ŲŖ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "Ł¾ŪŒŲ§ŲÆŁ‡ā€ŒŲ³Ų§Ų²ŪŒ Ś©ŪŒā€ŒŲÆŪŒā€ŒŲ§ŪŒ کانکت برای ŚÆŁ†ŁˆŁ…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "ŚÆŲ±ŁˆŁ‡ Ų¬ŪŒā€ŒŲ§Ų³ā€ŒŚ©Ų§Ł†Ś©ŲŖ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "" "GSConnect is a complete implementation of KDE Connect especially for GNOME " "Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team " "has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "" "Ų¬ŪŒā€ŒŲ§Ų³ā€ŒŚ©Ų§Ł†Ś©ŲŖŲŒ Ł¾ŪŒŲ§ŲÆŁ‡ā€ŒŲ³Ų§Ų²ŪŒ Ś©Ų§Ł…Ł„ŪŒ Ų§Ų² Ś©ŪŒā€ŒŲÆŪŒā€ŒŲ§ŪŒ Ś©Ų§Ł†Ś©ŲŖŲŒ Ł…Ų®ŲµŁˆŲµ Ł¾ŁˆŲ³ŲŖŁ‡Ł” ŚÆŁ†ŁˆŁ… ŲØŲ§ ŪŒŚ©Ł¾Ų§Ų±Ś†ŚÆŪŒ " "Ł†Ų§ŲŖŪŒŁ„ŁˆŲ³ŲŒ Ś©Ų±ŁˆŁ… و فایرفاکس Ų§Ų³ŲŖ. ŚÆŲ±ŁˆŁ‡ Ś©ŪŒā€ŒŲÆŪŒā€ŒŲ§ŪŒ Ś©Ų§Ł†Ś©ŲŖŲŒ ŲØŲ±Ł†Ų§Ł…Ł‡ā€ŒŁ‡Ų§ŪŒŪŒ برای ŚÆŁ†Łˆ/Ł„ŪŒŁ†ŁˆŚ©Ų³ŲŒ " "ŲØŪŒā€ŒŲ§Ų³ā€ŒŲÆŪŒŲŒ Ų§Ł†ŲÆŲ±ŁˆŪŒŲÆŲŒ Ų³ŪŒŁ„ā€ŒŁŪŒŲ³ŲŒ Ł…Ś© Ų§Łˆā€ŒŲ§Ų³ و ŁˆŪŒŁ†ŲÆŁˆŲ² دارند." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "" "With GSConnect you can securely connect to mobile devices and other desktops " "to:" msgstr "" "ŲØŲ§ Ų¬ŪŒā€ŒŲ§Ų³ā€ŒŚ©Ų§Ł†Ś©ŲŖ Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŪŒŲÆ به صورت امن به Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŁ‡Ų§ŪŒ همراه و دیگر Ł…ŪŒŲ²Ś©Ų§Ų±Ł‡Ų§ŪŒŲŖŲ§Ł† ŁˆŲµŁ„ " "ؓوید ŲŖŲ§:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Ł¾Ų±ŁˆŁ†ŲÆŁ‡ā€ŒŁ‡Ų§ŲŒ Ł¾ŪŒŁˆŁ†ŲÆŁ‡Ų§ و متن Ų±Ų§ Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ Ś©Ł†ŪŒŲÆ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Ł¾ŪŒŲ§Ł…ā€ŒŁ‡Ų§ Ų±Ų§ فرستاده و دریافت Ś©Ł†ŪŒŲÆ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Ł…Ų­ŲŖŁˆŲ§ŪŒ ŲŖŲ®ŲŖŁ‡ā€ŒŚÆŪŒŲ±Ł‡ Ų±Ų§ همگام Ś©Ł†ŪŒŲÆ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Ų¢Ų“Ł†Ų§ŪŒŲ§Ł† Ų±Ų§ همگام Ś©Ł†ŪŒŲÆ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Ų¢ŚÆŲ§Ł‡ŪŒā€ŒŁ‡Ų§ Ų±Ų§ همگام Ś©Ł†ŪŒŲÆ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Ł¾Ų®Ų“ā€ŒŚ©Ł†Ł†ŲÆŁ‡ā€ŒŁ‡Ų§ŪŒ رسانه Ų±Ų§ وابپایید" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "حجم صدای سامانه Ų±Ų§ وابپایید" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "ŲÆŲ³ŲŖŁˆŲ±Ł‡Ų§ŪŒ Ų§Ų² پیؓ ŲŖŲ¹Ų±ŪŒŁā€ŒŲ“ŲÆŁ‡ Ų±Ų§ Ų§Ų¬Ų±Ų§ Ś©Ł†ŪŒŲÆ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "و ŲØŪŒŲ“ā€ŒŲŖŲ±ā€¦" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "Ų¬ŪŒā€ŒŲ§Ų³ā€ŒŚ©Ų§Ł†Ś©ŲŖ ŲÆŲ± Ł¾ŁˆŲ³ŲŖŁ‡Ł” ŚÆŁ†ŁˆŁ…" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "ŁˆŲµŁ„ ؓدن به…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Ł„ŲŗŁˆ" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "ŁˆŲµŁ„ ؓدن" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "Ł†Ų“Ų§Ł†ŪŒ Ų¢ŪŒā€ŒŁ¾ŪŒ" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "ŲØŲÆŁˆŁ† آؓنا" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "راهنما" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "ؓماره تلفن یا Ł†Ų§Ł…ŪŒ Ų±Ų§ ŲØŪŒŲ§Ų²Ł…Ų§ŪŒŪŒŲÆ" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "دیگر" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "فرستادن Ł¾ŪŒŲ§Ł…Ś©" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "فرستادن" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "افزاره قطع Ų§Ų³ŲŖ" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "فرستادن Ł¾ŪŒŲ§Ł…" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Ł†ŁˆŲ“ŲŖŁ† یک Ł¾ŪŒŲ§Ł…" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "ورودی Ł¾ŪŒŲ§Ł…" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Ł¾ŪŒŲ§Ł…ŪŒ Ł†ŁˆŲ“ŲŖŁ‡ و برای ŁŲ±Ų³ŲŖŲ§ŲÆŁ†ŲŒ ورود Ų±Ų§ ŲØŲ²Ł†ŪŒŲÆ" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Ł¾ŪŒŲ§Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "ŚÆŁŲŖā€ŒŁˆŚÆŁˆŪŒ جدید" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "ŲØŲÆŁˆŁ† ŚÆŁŲŖā€ŒŁˆŚÆŁˆ" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "ŚÆŁŲŖā€ŒŁˆŚÆŁˆŪŒ ŚÆŲ²ŪŒŲÆŁ‡ نؓده" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "ŚÆŁŲŖā€ŒŁˆŚÆŁˆŪŒŪŒ Ų±Ų§ ŲØŲ±ŚÆŪŒŲÆŁ‡ یا Ų¢ŲŗŲ§Ų² Ś©Ł†ŪŒŲÆ" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "ویرایؓ دستور" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Ų°Ų®ŪŒŲ±Ł‡" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "نام" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Ų®Ų· فرمان" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Ł¾Ų±ŁˆŁ†ŲÆŁ‡Ł” Ų§Ų¬Ų±Ų§ŪŒŪŒā€ŒŲ§ŪŒ ŲØŲ±ŚÆŲ²ŪŒŁ†ŪŒŲÆ" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "ŚÆŲ“ŁˆŲÆŁ†" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Ł…ŪŒŲ²Ś©Ų§Ų±" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "ŲÆŁˆŲ±ŲØŪŒŁ†" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Ł‡Ł…ŚÆŲ§Ł…ā€ŒŲ³Ų§Ų²ŪŒ ŲŖŲ®ŲŖŁ‡ā€ŒŚÆŪŒŲ±Ł‡" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Ł¾Ų®Ų“ā€ŒŚ©Ł†Ł†ŲÆŁ‡ā€ŒŁ‡Ų§ŪŒ رسانه" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Ł…ŁˆŲ“ŪŒ و ŲµŁŲ­Ł‡ā€ŒŚ©Ł„ŪŒŲÆ" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "واپایؓ حجم ŲµŲÆŲ§" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Ł¾Ų±ŁˆŁ†ŲÆŁ‡ā€ŒŁ‡Ų§" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "دریافت Ł¾Ų±ŁˆŁ†ŲÆŁ‡ā€ŒŁ‡Ų§" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Ų°Ų®ŪŒŲ±Ł‡Ł” Ł¾Ų±ŁˆŁ†ŲÆŁ‡ā€ŒŁ‡Ų§ ŲÆŲ±" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "باتری افزاره" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Ų¢ŚÆŲ§Ł‡ŪŒ کم ŲØŁˆŲÆŁ† باتری" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "ŲŖŲ§ Ų¢ŚÆŲ§Ł‡ŪŒ Ų³Ų·Ų­ سفارؓی ؓارژ Ł…ŪŒā€ŒŲ“ŁˆŲÆ" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Ų¢ŚÆŲ§Ł‡ŪŒ پر ؓدن کامل" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "باتری سامانه" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ آمار" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "باتری" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "ŲÆŲ³ŲŖŁˆŲ±Ł‡Ų§" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Ų§ŁŲ²ŁˆŲÆŁ† دستور" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ Ų¢ŚÆŲ§Ł‡ŪŒā€ŒŁ‡Ų§" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ هنگام فعّال ŲØŁˆŲÆŁ† ŲÆŲ± نؓست" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "ŲØŲ±Ł†Ų§Ł…Ł‡ā€ŒŁ‡Ų§" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Ų¢ŚÆŲ§Ł‡ŪŒā€ŒŁ‡Ų§" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Ų¢Ų“Ł†Ų§ŪŒŲ§Ł†" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "ŲŖŁ…Ų§Ų³ā€ŒŁ‡Ų§ŪŒ دریافتی" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "حجم ŲµŲÆŲ§" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "مکث رسانه" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "ŲŖŁ…Ų§Ų³ā€ŒŁ‡Ų§ŪŒ ŲÆŲ± Ų­Ų§Ł„ انجام" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Ų®Ł…ŁˆŲ“ŪŒ Ł…ŪŒŚ©Ų±ŁˆŁŁˆŁ†" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "تلفن" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Ų§ŁŲ²ŁˆŲÆŁ† Ł…ŪŒŲ§Ł†ā€ŒŲØŲ±" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "ŲØŲ§Ų²Ł†Ų“Ų§Ł†ŪŒ همه…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Ł…ŪŒŲ§Ł†ā€ŒŲØŲ±Ł‡Ų§" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Ų§ŁŲ²Ų§ŪŒŁ‡ā€ŒŁ‡Ų§" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Ų¢Ų²Ł…Ų§ŪŒŲ“ŪŒ" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "انبارهٔ افزاره" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Ł¾Ų§Ś©ā€ŒŲ³Ų§Ų²ŪŒ انباره…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Ł¾Ų“ŲŖŪŒŲ§ŲØŁ† Ł¾ŪŒŲ§Ł…Ś© Ł‚ŲÆŪŒŁ…ŪŒ" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "سوار کردن خودکار SFTP" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "Ł¾ŪŒŲ“ā€ŒŲ±ŁŲŖŁ‡" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Ł…ŪŒŲ§Ł†ā€ŒŲØŲ±Ł‡Ų§ŪŒ ŲµŁŲ­Ł‡ā€ŒŚ©Ł„ŪŒŲÆ" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ افزاره" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "جفت کردن" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "افزاره Ų¬ŲÆŲ§ ؓده" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "ممکن Ų§Ų³ŲŖ ŲØŲ®ŁˆŲ§Ł‡ŪŒŲÆ پیؓ Ų§Ų² جفت Ś©Ų±ŲÆŁ†ŲŒ Ų§ŪŒŁ† افزاره Ų±Ų§ Ł¾ŪŒŚ©Ų±ŲØŁ†ŲÆŪŒ Ś©Ł†ŪŒŲÆ" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "اطّلاعات Ų±Ł…Ų²Ł†ŚÆŲ§Ų±ŪŒ" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Ų¬ŲÆŲ§ سازی" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "به افزاره" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Ų§Ų² افزاره" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Ł‡ŪŒŚ†" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "ŲØŲ§Ų²ŚÆŲ±ŲÆŲ§Ł†ŪŒ" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "کم کردن" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Ų®Ł…ŁˆŲ“" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "ŲŖŁ†ŲøŪŒŁ…" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "برای ŲØŲ§Ų²Ł†Ų“Ų§Ł†ŪŒ Ł…ŪŒŲ§Ł†ā€ŒŲØŲ± ŲµŁŲ­Ł‡ā€ŒŚ©Ł„ŪŒŲÆŲŒ گریز یا Ł¾Ų³ā€ŒŲØŲ± Ų±Ų§ ŲØŲ²Ł†ŪŒŲÆ." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "نام افزاره" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_تغییر نام" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Ł†ŁˆŲ³Ų§Ų²ŪŒ" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ تلفن همراه" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "فهرست Ų®ŲÆŁ…ŲŖ" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "فهرست افزاره" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "ویرایؓ نام افزاره" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŁ‡Ų§" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "ŲÆŲ± Ų¬Ų³ŲŖā€ŒŁˆŲ¬ŁˆŪŒ Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŁ‡Ų§ā€¦" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Ų§ŁŲ²ŁˆŁ†Ł‡ā€ŒŁ‡Ų§ŪŒ Ł…Ų±ŁˆŲ±ŚÆŲ±" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "به کار انداختن" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Ų§ŪŒŁ† افزاره برای Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŁ‡Ų§ŪŒ جفت Ł†Ų“ŲÆŁ‡ŲŒ Ł†Ų§Ł…Ų±ŪŒŪŒ Ų§Ų³ŲŖ" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "کؓف Ų§Ų² کار افتاد" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "حالت Ł†Ł…Ų§ŪŒŲ“" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "ŲŖŲ§ŲØŁ„Łˆ" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "فهرست کاربر" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "ایجاد ŚÆŲ²Ų§Ų±Ų“ Ł¾Ų“ŲŖŪŒŲØŲ§Ł†ŪŒ" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "دربارهٔ Ų¬ŪŒā€ŒŲ§Ų³ā€ŒŚ©Ų§Ł†Ś©ŲŖ" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŲ§ŪŒ Ų±Ų§ ŲØŲ±ŚÆŲ²ŪŒŁ†ŪŒŲÆ" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "ŚÆŲ²ŪŒŁ†Ų“" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Ł‡ŪŒŚ† Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŲ§ŪŒ پیدا نؓد" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "فهرست افزاره" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "ŚÆŲ²Ų§Ų±Ų“" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Ś†ŪŒŲ²ŪŒ اؓتباه Ų“ŲÆ" #: data/ui/service-error-dialog.ui:84 msgid "" "GSConnect encountered an unexpected error. Please report the problem and " "include any information that may help." msgstr "" "Ų¬ŪŒā€ŒŲ§ŪŒā€ŒŚ©Ų§Ł†Ś©ŲŖ ŲØŲ§ خطایی ŲŗŪŒŲ±Ł…Ł†ŲŖŲøŲ±Ł‡ Ų±ŁˆŲØŁ‡ā€ŒŲ±Łˆ Ų“ŲÆ. لطفاً مؓکل Ų±Ų§ ŚÆŲ²Ų§Ų±Ų“ داده و هر " "Ų§Ų·Ł‘Ł„Ų§Ų¹Ų§ŲŖŪŒ که ممکن Ų§Ų³ŲŖ کمک کند Ų±Ų§ ŲØŲÆŁ‡ŪŒŲÆ." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "جزییات ŁŁ†ŪŒ" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "فرستادن به افزارهٔ همراه" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŁ‡Ų§ŪŒ همراه" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Ų±ŁˆŲ“Ł† کردن" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d ŁˆŲµŁ„ā€ŒŲ“ŲÆŁ‡" msgstr[1] "%d ŁˆŲµŁ„ā€ŒŲ“ŲÆŁ‡" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Ų®Ų§Ł…ŁˆŲ“ کردن" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "ویرایؓ" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "برداؓتن" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Ų§Ų² کار افتاده" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "برای تغییر %s Ł…ŪŒŲ§Ł†ā€ŒŲØŲ± جدیدی وارد Ś©Ł†ŪŒŲÆ" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s Ų§Ų² پیؓ ŲÆŲ± Ų­Ų§Ł„ استفاده Ų§Ų³ŲŖ" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Ł¾ŪŒŲ§ŲÆŁ‡ā€ŒŲ³Ų§Ų²ŪŒ کامل Ś©ŪŒā€ŒŲÆŪŒā€ŒŲ§ŪŒ کانکت برای ŚÆŁ†ŁˆŁ…" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "ŲÆŲ§Ł†ŪŒŲ§Ł„ ŲØŁ‡Ų²Ų§ŲÆŪŒ " #: src/preferences/service.js:411 msgid "" "Debug messages are being logged. Take any steps necessary to reproduce a " "problem then review the log." msgstr "" "Ł¾ŪŒŲ§Ł…ā€ŒŁ‡Ų§ŪŒ رفع اؓکال Ų«ŲØŲŖ Ų“ŲÆŁ‡ā€ŒŲ§Ł†ŲÆ. هر Ų§Ł‚ŲÆŲ§Ł…ŪŒ که برای ŲØŲ§Ų²ŲŖŁˆŁ„ŪŒŲÆ مؓکل لازم Ų§Ų³ŲŖ Ų±Ų§ " "انجام ŲÆŲ§ŲÆŁ‡ŲŒ سپس ŚÆŲ²Ų§Ų±Ų“ Ų±Ų§ بررسی Ś©Ł†ŪŒŲÆ." #: src/preferences/service.js:414 msgid "Review Log" msgstr "ŲØŲ§Ų²ŲØŪŒŁ†ŪŒ ŚÆŲ²Ų§Ų±Ų“" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Ł„Ł¾ā€ŒŲŖŲ§Ł¾" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "تلفن Ł‡ŁˆŲ“Ł…Ł†ŲÆ" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Ų±Ų§ŪŒŲ§Ł†Ś©" #: src/preferences/service.js:488 msgid "Television" msgstr "ŲŖŁ„ŁˆŪŒŲ²ŪŒŁˆŁ†" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Ų¬ŲÆŲ§ ؓده" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "قطع ؓده" #: src/preferences/service.js:518 msgid "Connected" msgstr "ŁˆŲµŁ„ ؓده" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "ŲÆŲ± انتظار خدمت…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "برای Ų±Ų§Ł‡Ł†Ł…Ų§ŪŒŪŒ برای Ų¹ŪŒŲØā€ŒŪŒŲ§ŲØŪŒ Ś©Ł„ŪŒŚ© Ś©Ł†ŪŒŲÆ" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "برای اطّلاعات بیؓتر Ś©Ł„ŪŒŚ© Ś©Ł†ŪŒŲÆ" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Ų“Ł…Ų§Ų±Ł‡ā€ŒŚÆŪŒŲ±ŪŒ" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "هم Ų±Ų³Ų§Ł†ŪŒ Ł¾Ų±ŁˆŁ†ŲÆŁ‡" #: src/service/daemon.js:351 msgid "List available devices" msgstr "فهرست Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŁ‡Ų§ŪŒ Ł…ŁˆŲ¬ŁˆŲÆ" #: src/service/daemon.js:360 msgid "List all devices" msgstr "فهرست ŲŖŁ…Ų§Ł…ŪŒ Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŁ‡Ų§" #: src/service/daemon.js:369 msgid "Target Device" msgstr "افزارهٔ هدف" #: src/service/daemon.js:411 msgid "Message Body" msgstr "متن Ł¾ŪŒŲ§Ł…" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "فرستادن Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "نام کارهٔ Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "متن Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "نقؓک Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "ؓناسهٔ Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "عکس" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ł¾ŪŒŁ†ŚÆ" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "زنگ" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ Ł¾ŪŒŁˆŁ†ŲÆ" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ متن" #: src/service/daemon.js:528 msgid "Show release version" msgstr "Ł†Ł…Ų§ŪŒŲ“ نگارؓ ارائه" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Ł†Ų§Ł…ŁˆŲ¬ŁˆŲÆ" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "دستگاه ŲØŁ„ŁˆŲŖŁˆŲ« ŲÆŲ± %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "اثرانگؓت %s:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "درخواست جفت کردن Ų§Ų² %s" #: src/service/device.js:800 msgid "Reject" msgstr "Ų±ŲÆ کردن" #: src/service/device.js:805 msgid "Accept" msgstr "پذیرؓ" #: src/service/manager.js:114 msgid "" "Discovery has been disabled due to the number of devices on this network." msgstr "به Ų®Ų§Ų·Ų± ŲŖŲ¹ŲÆŲ§ŲÆ Ų§ŁŲ²Ų§Ų±Ł‡ā€ŒŁ‡Ų§ŪŒ روی Ų§ŪŒŁ† Ų“ŲØŚ©Ł‡ŲŒ کؓف Ų§Ų² کار افتاد." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "Ų§ŁˆŁ¾Ł†ā€ŒŲ§Ų³ā€ŒŲ§Ų³ā€ŒŲ§Ł„ پیدا نؓد" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "درگاه Ų§Ų² پیؓ ŲÆŲ± Ų­Ų§Ł„ استفاده Ų§Ų³ŲŖ" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: باتری پر Ų§Ų³ŲŖ" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "ؓارژ کامل Ų“ŲÆ" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" # ŲÆŲ± Ų§ŪŒŁ† رؓته Ų§Ų² Ł†ŁˆŪŒŲ³Ł‡ā€ŒŁ‡Ų§ŪŒ Ų§ŪŒŲ²ŁˆŁ„Ł‡ Ś†Ł¾ā€ŒŲØŁ‡ā€ŒŲ±Ų§Ų³ŲŖ استفاده ؓده Ų§Ų³ŲŖ #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "⁦%d٪⁩ ؓارژ Ų“ŲÆ" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: باتری کم Ų§Ų³ŲŖ" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "⁦%d٪⁩ مانده" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "ŲŖŲ®ŲŖŁ‡ā€ŒŚÆŪŒŲ±Ł‡" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "Ł…Ų­ŲŖŁˆŲ§ŪŒ ŲŖŲ®ŲŖŁ‡ā€ŒŚÆŪŒŲ±Ł‡ Ų±Ų§ Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ Ś©Ł†ŪŒŲÆ" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "فرستادن به ŲŖŲ®ŲŖŁ‡ā€ŒŚÆŪŒŲ±Ł‡" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "گرفتن Ų§Ų² ŲŖŲ®ŲŖŁ‡ā€ŒŚÆŪŒŲ±Ł‡" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "به Ł…Ų®Ų§Ų·ŲØŪŒŁ† دستگاه Ų¬ŁŲŖā€ŒŲ³Ų§Ų²ŪŒ ؓده دسترسی پیدا Ś©Ł†ŪŒŲÆ" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "تلفنم Ų±Ų§ بیاب" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "زنگ Ų®ŁˆŲ±ŲÆŁ† دستگاه جفت ؓده Ų“Ł…Ų§" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "صفحهٔ Ł…ŁˆŲ“ŪŒ" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "ŲµŁŲ­Ł‡ā€ŒŚ©Ł„ŪŒŲÆ" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "ناؓناخته" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Ł„ŲŗŁˆ Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "بستن Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "پاسخ به Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "فعال سازی Ų¢ŚÆŲ§Ł‡ŪŒ" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Ų¬Ų§ŲØŁ‡ā€ŒŲ¬Ų§ŪŒŪŒ ؓکست خورد" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "ؓکست ŲÆŲ± فرستادن Ā«%sĀ» به %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "Ł¾ŪŒŁ†ŚÆā€ŒŁ‡Ų§ Ų±Ų§ فرستاده و دریافت Ś©Ł†ŪŒŲÆ" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ł¾ŪŒŁ†ŚÆ: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "ارائه" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "اجرای ŲÆŲ³ŲŖŁˆŲ±Ł‡Ų§" #: src/service/plugins/runcommand.js:13 msgid "" "Run commands on your paired device or let the device run predefined commands " "on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "سوار کردن" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Ł¾ŪŒŲ§ŲÆŁ‡ کردن" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s خطایی ŚÆŲ²Ų§Ų±Ų“ کرد" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ Ł¾Ų±ŁˆŁ†ŲÆŁ‡ā€ŒŁ‡Ų§ و Ł†Ų“Ų§Ł†ŪŒā€ŒŁ‡Ų§ Ł…ŪŒŲ§Ł† ŲÆŲ³ŲŖŚÆŲ§Ł‡ā€ŒŁ‡Ų§" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s اجازهٔ بارگذاری Ł¾Ų±ŁˆŁ†ŲÆŁ‡ā€ŒŁ‡Ų§ Ų±Ų§ ندارد" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "ŲÆŲ± Ų­Ų§Ł„ Ų¬Ų§ŲØŁ‡ā€ŒŲ¬Ų§ŪŒŪŒ Ł¾Ų±ŁˆŁ†ŲÆŁ‡ā€ŒŁ‡Ų§" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "ŲÆŲ± Ų­Ų§Ł„ دریافت Ā«%sĀ» Ų§Ų² %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Ų¬Ų§ŲØŁ‡ā€ŒŲ¬Ų§ŪŒŪŒ Ł…ŁˆŁŁ‚" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "Ā«%sĀ» Ų§Ų² %s دریافت Ų“ŲÆ" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "ŚÆŲ“ŁˆŲÆŁ† ؓاخه" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "ŚÆŲ“ŁˆŲÆŁ† Ł¾Ų±ŁˆŁ†ŲÆŁ‡" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "ؓکست ŲÆŲ± گرفتن Ā«%sĀ» Ų§Ų² %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "متن Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŲÆŁ‡ Ų§Ų² %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "ŲÆŲ± Ų­Ų§Ł„ فرستادن Ā«%sĀ» به %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "Ā«%sĀ» به %s فرستاده Ų“ŲÆ" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "فرستادن Ł¾Ų±ŁˆŁ†ŲÆŁ‡ā€ŒŁ‡Ų§ به %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "ŚÆŲ“ŁˆŲÆŁ† هنگام Ų§ŲŖŁ…Ų§Ł…" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "فرستادن Ł¾ŪŒŁˆŁ†ŲÆŪŒ به %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "Ł¾ŪŒŲ§Ł…Ś©" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Ł¾ŪŒŲ§Ł…Ś© جدید (Ł†Ų“Ų§Ł†ŪŒ)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "پاسخ به Ł¾ŪŒŲ§Ł…Ś©" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ Ł¾ŪŒŲ§Ł…Ś©" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "حجم صدای سامانه" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "Ł¾Ų§Ł„Ų³ā€ŒŲ¢ŲÆŪŒŁˆ پیدا نؓد" #: src/service/plugins/telephony.js:14 msgid "" "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Ų®Ł…ŁˆŲ“ŪŒ ŲŖŁ…Ų§Ų³" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Ų¢Ų“Ł†Ų§ŪŒ ناؓناس" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "ŲŖŁ…Ų§Ų³ دریافتی" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "ŲŖŁ…Ų§Ų³ جاری" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "ŲÆŁˆŲ±Ł†ŚÆŲ§Ų±" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "کاری" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "همراه" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "خانه" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "فرستادن به %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Ł‡Ł…ā€ŒŲ§Ś©Ł†ŁˆŁ†" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "دیروز 惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d ŲÆŁ‚ŪŒŁ‚Ł‡" msgstr[1] "%d ŲÆŁ‚ŪŒŁ‚Ł‡" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Ł¾ŪŒŲ§Ł… ŚÆŲ±ŁˆŁ‡ŪŒ" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Ų“Ł…Ų§: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "و %d Ų¢Ų“Ł†Ų§ŪŒ دیگر" msgstr[1] "و %d Ų¢Ų“Ł†Ų§ŪŒ دیگر" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "ŲµŁŲ­Ł‡ā€ŒŚ©Ł„ŪŒŲÆ دوردست روی %s فعّال Ł†ŪŒŲ³ŲŖ" # ŲÆŲ± Ų§ŪŒŁ† رؓته Ų§Ų² Ł†ŁˆŪŒŲ³Ł‡ā€ŒŁ‡Ų§ŪŒ Ų§ŪŒŲ²ŁˆŁ„Ł‡ Ś†Ł¾ā€ŒŲØŁ‡ā€ŒŲ±Ų§Ų³ŲŖ استفاده ؓده Ų§Ų³ŲŖ #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "⁦%d٪⁩ (ŲÆŲ± Ų­Ų§Ł„ محاسبه…)" # ŲÆŲ± Ų§ŪŒŁ† رؓته Ų§Ų² Ł†ŁˆŪŒŲ³Ł‡ā€ŒŁ‡Ų§ŪŒ Ų§ŪŒŲ²ŁˆŁ„Ł‡ Ś†Ł¾ā€ŒŲØŁ‡ā€ŒŲ±Ų§Ų³ŲŖ استفاده ؓده Ų§Ų³ŲŖ #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "⁦%d٪⁩ (⁦%d:%02d⁩ ŲŖŲ§ پر ؓدن)" # ŲÆŲ± Ų§ŪŒŁ† رؓته Ų§Ų² Ł†ŁˆŪŒŲ³Ł‡ā€ŒŁ‡Ų§ŪŒ Ų§ŪŒŲ²ŁˆŁ„Ł‡ Ś†Ł¾ā€ŒŲØŁ‡ā€ŒŲ±Ų§Ų³ŲŖ استفاده ؓده Ų§Ų³ŲŖ #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "⁦%d٪⁩ (⁦%d:%02d⁩ مانده)" #: src/shell/notification.js:54 msgid "Reply" msgstr "پاسخ" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "ŲØŲ§ Ų¬ŪŒā€ŒŲ§Ų³ Ś©Ų§Ł†Ś©ŲŖŲŒ Ł¾ŪŒŁˆŁ†ŲÆŁ‡Ų§ Ų±Ų§ ŲØŲ§ Ł¾ŪŒŲ§Ł…Ś© یا Ł…Ų³ŲŖŁ‚ŪŒŁ…Ų§Ł‹ ŲÆŲ± Ł…Ų±ŁˆŲ±ŚÆŲ± Ł‡Ł…ā€ŒŲ±Ų³Ų§Ł†ŪŒ Ś©Ł†ŪŒŲÆ." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Ų®ŲÆŁ…ŲŖ Ł†Ų§Ł…ŁˆŲ¬ŁˆŲÆ" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "ŚÆŲ“ŁˆŲÆŁ† ŲÆŲ± Ł…Ų±ŁˆŲ±ŚÆŲ±" #~ msgid "On" #~ msgstr "Ų±ŁˆŲ“Ł†" #~ msgid "Off" #~ msgstr "Ų®Ų§Ł…ŁˆŲ“" gnome-shell-extension-gsconnect-50/po/fi.po000066400000000000000000001037721421543444100210750ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the org.gnome.Shell.Extensions.GSConnect package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: org.gnome.Shell.Extensions.GSConnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-10-01 21:07+0300\n" "Last-Translator: Elias Arno Eskelinen \n" "Language-Team: \n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 2.4.2\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "KDE Connect -toteutus GNOMElle" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "GSConnect -tiimi" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "" "GSConnect is a complete implementation of KDE Connect especially for GNOME " "Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team " "has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "" "GSConnect on tƤydellinen toteutus KDE Connectista erityisesti GNOME " "Shellille Nautilus-, Chrome- ja Firefox-integraatiolla. KDE Connect -" "tiimillƤ on sovelluksia Linuxille, BSD:lle, Androidille, Sailfishille, macOS:" "lle ja Windowsille." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "" "With GSConnect you can securely connect to mobile devices and other desktops " "to:" msgstr "" "GSConnectin avulla voit muodostaa turvallisen yhteyden mobiililaitteisiin ja " "muihin tietokoneisiin:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Jaa tiedostoja, linkkejƤ ja tekstiƤ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "LƤhetƤ ja vastaanota viestejƤ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Synkronoi leikepƶydƤn sisƤltƶ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Synkronoi yhteystiedot" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Synkronoi ilmoitukset" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Ohjaa mediasoittimia" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "Ohjaa jƤrjestelmƤn ƤƤnenvoimakkuutta" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Suorita ennaltamƤƤrƤttyjƤ komentoja" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "Ja lisää…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect GNOME Shell:issƤ" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "YhdistƤ kohteeseen…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Peruuta" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "YhdistƤ" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP-osoite" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "Ei yhteystietoja" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Apua" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Kirjoita puhelinnumero tai nimi" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Muut" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "LƤhetƤ tekstiviestillƤ" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "LƤhetƤ" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Laite on kytketty irti" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "LƤhetƤ viesti" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Kirjoita viesti" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "Viestin syƶttƶ" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Kirjoita viesti ja paina EnteriƤ lƤhettƤƤksesi" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "ViestintƤ" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Uusi Keskustelu" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Ei Keskusteluja" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Ei Valittuja Keskusteluja" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Valitse tai aloita keskustelu" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Muokkaa komentoa" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Tallenna" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Nimi" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Komentorivi" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Valitse kƤynnistystiedosto" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Avaa" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "TyƶpƶytƤ" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "Kamera" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "LeikepydƤn Synkronointi" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Mediasoittimet" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Hiiri & NƤppƤimistƶ" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "ƄƤnenvoimakkuuden SƤƤtƶ" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Tiedostot" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Vastaanota Tiedostoja" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Tallenna tiedostot nimellƤ" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Jakaminen" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Laitteen Akku" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Ilmoitus Akun Alhaisesta Varaustasosta" #: data/ui/preferences-device-panel.ui:699 #, fuzzy msgid "Charged Up to Custom Level Notification" msgstr "Ilmoitus Akun TƤydestƤ Varaustasosta" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Ilmoitus Akun TƤydestƤ Varaustasosta" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "JƤrjestelmƤn Akku" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Jaa Tilastot" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Akku" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Komennot" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "LisƤƤ komento" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Jaa Ilmoitukset" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Jaa, Kun Aktiivinen" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Sovellukset" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Ilmoitukset" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Yhteystiedot" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Tulevat Puhelut" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "ƄƤnenvoimakkuus" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "KeskeytƤ Media" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "MeneillƤƤn Olevat Puhelut" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "MykistƤ Mikrofoni" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Puhelinpalvelut" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Toiminto-pikanƤppƤimet" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Nollaa Kaikki…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Pikakomennot" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "LisƤosat" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Kokeelliset" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Laitteen VƤlimuisti" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "TyhjennƤ VƤlimuisti…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Vanhentunut SMS-tuki" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "SFTP:n Automaattinen LiitƤntƤ" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "LisƤominaisuudet" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "NƤppƤimistƶn PikanƤppƤimet" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "Laitteen Asetukset" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Parita" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "Laitteen paritus on poistettu" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Voit mƤƤrittƤƤ tƤmƤn laitteen ennen pariliitoksen muodostamista" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Salaustiedot" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Poista Paritus" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Laitteelle" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Laitteesta" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Ei mitƤƤn" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Palauta" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Alenna" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "MykistƤ" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Aseta" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Paina Esc peruuttaaksesi tai Backspace nollataksesi pikanƤppƤimen." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Laitteen Nimi" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_UudelleennimeƤ" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "VirkistƤ" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "Mobiiliasetukset" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Palvelun Valikko" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "Laitteen Valikko" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Muokkaa Laitteen NimeƤ" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Laitteet" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "EtsitƤƤn laitteita…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Selain-lisƤosat" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Ota kƤyttƶƶn" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "TƤmƤ laite on nƤkymƤtƶn parittomille laitteille" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Laitteiden lƶytƶ pois kƤytƶstƤ" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "NƤyttƶtila" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Paneeli" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "KƤyttƤjƤvalikko" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Luo tukiloki" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "Tietoja GSConnectista" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Valitse Laite" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Valitse" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Laitetta Ei Lƶytynyt" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Laiteluettelo" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Ilmoita" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Jokin meni pieleen" #: data/ui/service-error-dialog.ui:84 msgid "" "GSConnect encountered an unexpected error. Please report the problem and " "include any information that may help." msgstr "" "GSConnect havaitsi odottamattoman virheen. Ilmoita ongelmasta ja liitƤ " "mukaan kaikki mahdolliset tiedot." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Tekniset yksityiskohdat" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "LƤhetƤ mobiililaitteeseen" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Mobiililaitteet" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Laita PƤƤlle" #: src/extension.js:232 #, fuzzy, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "Yhdistetty" msgstr[1] "Yhdistetty" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 #, fuzzy msgid "Turn Off" msgstr "Laita PƤƤlle" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Muokkaa" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Poista" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Poistettu KƤytƶstƤ" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Kirjoita uusi pikanƤppƤin muuttaaksesi %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s on jo kƤytƶssƤ" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "TƤydellinen KDE Connect -toteutus GNOMEa varten" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "kƤƤntƤjƤt" #: src/preferences/service.js:411 msgid "" "Debug messages are being logged. Take any steps necessary to reproduce a " "problem then review the log." msgstr "" "VianmƤƤritysviestit kirjataan. Tee kaikki tarvittavat toimenpiteet ongelman " "toistamiseksi ja tarkista sitten loki." #: src/preferences/service.js:414 msgid "Review Log" msgstr "Tarkista loki" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Kannettava" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Ƅlypuhelin" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tabletti" #: src/preferences/service.js:488 msgid "Television" msgstr "Televisio" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "EpƤparitettu" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Yhteys katkennut" #: src/preferences/service.js:518 msgid "Connected" msgstr "Yhdistetty" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "Odotetaan palvelua…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "NƤppƤile Numero" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Jaa Tiedosto" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Listaa saatavilla olevat laitteet" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Listaa kaikki laitteet" #: src/service/daemon.js:369 msgid "Target Device" msgstr "Kohdelaite" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Viestin runko" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "LƤhetƤ Ilmoitus" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "Ilmoitussovelluksen nimi" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Ilmoituksen Runko" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Ilmoituksen Kuvake" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "Ilmoituksen Tunnus" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Kuva" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Soita ƄƤni" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Jaa Linkki" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Jaa Teksti" #: src/service/daemon.js:528 msgid "Show release version" msgstr "NƤytƤ julkaisuversio" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Ei saatavilla" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Parituspyyntƶ kohteelta %s" #: src/service/device.js:800 msgid "Reject" msgstr "HylkƤƤ" #: src/service/device.js:805 msgid "Accept" msgstr "HyvƤksy" #: src/service/manager.js:114 msgid "" "Discovery has been disabled due to the number of devices on this network." msgstr "" "Laitehaku on poistettu kƤytƶstƤ tƤssƤ verkossa olevien laitteiden lukumƤƤrƤn " "vuoksi." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSL:ƤƤ ei lƶytynyt" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "Portti on jo kƤytƶssƤ" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: Akku tƤynnƤ" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "TƤysin Ladattu" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, fuzzy, javascript-format msgid "%d%% Charged" msgstr "TƤysin Ladattu" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: Akku lƤhes tyhjƤ" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% jƤljellƤ" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "LeikepƶytƤ" #: src/service/plugins/clipboard.js:11 #, fuzzy msgid "Share the clipboard content" msgstr "Synkronoi leikepƶydƤn sisƤltƶ" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "LeikepƶydƤn Tyƶntƶ" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "LeikepƶydƤn Veto" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "LƶydƤ Puhelimeni" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "Hiirimatto" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "NƤppƤimistƶ" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "Tuntematon" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Peruuta Ilmoitus" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Sulje Ilmoitus" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "Vastaa Ilmoitukseen" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Aktivoi Ilmoitus" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Siirto EpƤonnistui" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "ā€œ%sā€ lƤhettƤminen kohteelle %s epƤonnistui" #: src/service/plugins/ping.js:12 #, fuzzy msgid "Send and receive pings" msgstr "LƤhetƤ ja vastaanota viestejƤ" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Pingaa: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "Esitys" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Suorita Komentoja" #: src/service/plugins/runcommand.js:13 msgid "" "Run commands on your paired device or let the device run predefined commands " "on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "LiitƤ" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Irroita" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Jaa" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "Kohteella %s ei ole lupaa lƤhettƤƤ tiedostoja" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "SiirretƤƤn Tiedostoa" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "Vastaanotetaan ā€œ%sā€ kohteelta %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Siirto Onnistui" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "Vastaanotettiin ā€œ%sā€ kohteelta %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Avaa Kansio" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Avaa Tiedosto" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "ā€œ%sā€ vastaanotto kohteelta %s epƤonnistui" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Teksti, jonka on jakanut %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "LƤhetetƤƤn ā€œ%sā€ kohteelle %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "LƤhetettiiin ā€œ%sā€ kohteelle %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "LƤhetƤ tiedostoja kohteelle %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Avaa, kun valmis" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "LƤhetƤ linkki kohteelle %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "Tekstiviesti" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Uusi Tekstiviesti (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "Vastaa Tekstiviestiin" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "Jaa Tekstiviesti" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "JƤrjestelmƤn ƄƤnenvoimakkuus" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudiota ei lƶytynyt" #: src/service/plugins/telephony.js:14 msgid "" "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "MykistƤ Puhelu" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Tuntematon Yhteystieto" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Tuleva Puhelu" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "MeneillƤƤn oleva puhelu" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Faxi" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Tyƶ" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Mobiili" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Koti" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "LƤhetƤ kohteelle %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Juuri nyt" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Eilen惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minuutti" msgstr[1] "%d minuuttia" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "RyhmƤviesti" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "SinƤ: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "Ja %d toinen yhteystieto" msgstr[1] "Ja %d muuta" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "EtƤnƤppƤimistƶ kohteella %s ei ole aktiivinen" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (Arvioidaan…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d∶%02d Kunnes TƤynnƤ)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d∶%02d JƤljellƤ)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Vastaa" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Jaa linkkejƤ GSConnectin avulla suoraan selaimeen tai tekstiviestillƤ." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Palvelu ei ole kƤytettƤvissƤ" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Avaa Selaimessa" #~ msgid "On" #~ msgstr "PƤƤllƤ" #~ msgid "Off" #~ msgstr "Pois PƤƤltƤ" gnome-shell-extension-gsconnect-50/po/fr.po000066400000000000000000001042351421543444100211010ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-09 01:50\n" "Last-Translator: \n" "Language-Team: French\n" "Language: fr_FR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: fr\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "Une implĆ©mentation de KDE Connect pour GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "Ɖquipe GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnect est une implĆ©mentation complĆØte de KDE Connect, en particulier pour GNOME Shell avec l'intĆ©gration de Nautilus, Chrome et Firefox. L'Ć©quipe KDE Connect a des applications pour Linux, BSD, Android, Sailfish, macOS et Windows." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "Avec GSConnect, vous pouvez vous connecter en toute sĆ©curitĆ© Ć  des appareils mobiles et Ć  d'autres ordinateurs de bureau Ć  :" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Partager des fichiers, des liens et du texte" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Envoyer et recevoir des messages" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Synchroniser le contenu du presse-papiers" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Synchroniser les contacts" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Synchroniser les notifications" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "ContrĆ“ler les lecteurs de mĆ©dias" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "ContrĆ“le le volume du systĆØme" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "ExĆ©cuter des commandes prĆ©dĆ©finies" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "Et bien plus encore…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect dans GNOME Shell" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Se connecter à…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Annuler" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Connecter" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "AdresseĀ IP" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "\"Aucun contact\"" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Aide" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Taper un numĆ©ro de tĆ©lĆ©phone ou un nom" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Autre" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "Envoyer un SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Envoyer" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "L'appareil est dĆ©connectĆ©" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Envoyer le message" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Taper un message" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "EntrĆ©e de message" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Tapez un message et appuyez sur EntrĆ©e pour envoyer" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Messagerie" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Nouvelle conversation" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Aucune discussion" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Aucune conversation sĆ©lectionnĆ©e" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Choisir ou commencer une discussion" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Modifier la commande" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Enregistrer" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Nom" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Commande" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Choisir un exĆ©cutable" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Ouvrir" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Ordinateur de bureau" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "Appareil photo" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Synchroniser le presse-papiers" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Lecteurs multimĆ©dia" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Souris et clavier" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Gestion du volume" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Fichiers" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Recevoir les fichiers" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Enregistrer les fichiers dans" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Partage" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Batterie de l'appareil" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Notification de batterie faible" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Notification entiĆØrement chargĆ©e" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Batterie systĆØme" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Partager les statistiques" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Batterie" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Commandes" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Ajouter une commande" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Synchroniser les notifications" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Partager quand actif" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Applications" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Notifications" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Contacts" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Appels entrants" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Volume" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Mettre les mĆ©dias en pause" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Appels en cours" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Mettre le microphone en sourdine" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "TĆ©lĆ©phonie" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Raccourcis d'actions" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Tout rĆ©initialiser…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Raccourcis" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Greffons" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "ExpĆ©rimental" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Cache de l'appareil" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Vider le cache…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Ancienne gestion des SMS" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "Montage automatique SFTP" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "AvancĆ©" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Raccourcis clavier" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "ParamĆØtres de l'appareil" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Associer" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "L'appareil n'est pas pairĆ©" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Vous pouvez configurer cet appareil avant le pairage" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Informations de chiffrement" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Dissocier" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Vers l'Appareil" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Depuis l'Appareil" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Ne rien faire" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Restaurer" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "RĆ©duire" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Mettre en sourdine" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "DĆ©finir" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Appuyez sur Echap pour annuler ou sur Retour ArriĆØre pour rĆ©initialiser le raccourci clavier." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Nom de l'appareil" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_Renommer" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "RafraĆ®chir" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "ParamĆØtres" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Menu du service" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "Menu de l'appareil" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Modifier le nom de l'appareil" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Appareils" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Recherche d'appareils…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Extensions du navigateur" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Activer" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Cet appareil est invisible par les appareils non pairĆ©s" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "DĆ©couverte dĆ©sactivĆ©e" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Mode d'affichage" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Barre des tĆ¢ches" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "Menu utilisateur" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "GĆ©nĆ©rer des logs pour le support" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "ƀ propos de GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "SĆ©lectionner un appareil" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "SĆ©lectionner" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Aucun appareil trouvĆ©" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Liste des appareils" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Signaler" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Quelque chose s'est mal passĆ©" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "GSConnect a rencontrĆ© une erreur inattendue. Veuillez signaler le problĆØme et inclure toutes les informations qui pourraient nous aider." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "DĆ©tails techniques" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "Envoyer vers l'appareil mobile" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Appareils mobiles" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Activer" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d ConnectĆ©" msgstr[1] "%d ConnectĆ©s" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "DĆ©sactiver" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Modifier" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Supprimer" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "DĆ©sactivĆ©" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Entrer un nouveau raccourci pour modifier %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s est dĆ©jĆ  utilisĆ©" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Une implĆ©mentation complĆØte de KDE Connect pour GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "MickaĆ«l Coiraton " #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "Les messages de dĆ©pannage sont enregistrĆ©s. Faites le nĆ©cessaire pour reproduire le problĆØme puis vĆ©rifiez le fichier de log." #: src/preferences/service.js:414 msgid "Review Log" msgstr "VĆ©rifier les logs" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Ordinateur portable" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Smartphone" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tablette" #: src/preferences/service.js:488 msgid "Television" msgstr "TĆ©lĆ©vision" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "DissociĆ©" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "DĆ©connectĆ©" #: src/preferences/service.js:518 msgid "Connected" msgstr "ConnectĆ©" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "En attente du service…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "Cliquer pour l'aide de dĆ©pannage" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "Cliquer pour plus d'informations" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Composer le numĆ©ro" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Partager un fichier" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Liste des appareils disponibles" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Liste de tous les appareils" #: src/service/daemon.js:369 msgid "Target Device" msgstr "Appareil cible" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Corps du message" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Envoyer la notification" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "Nom de l'application de la notification" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Corps de la notification" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "IcĆ“ne de la notification" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "ID de la notification" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Photo" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Faire sonner" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Partager un lien" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Partager du texte" #: src/service/daemon.js:528 msgid "Show release version" msgstr "Montrer la sortie de version" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Indisponible" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "PĆ©riphĆ©rique Bluetooth Ć  %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "Empreinte %s:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Demande d'association depuis %s" #: src/service/device.js:800 msgid "Reject" msgstr "Rejeter" #: src/service/device.js:805 msgid "Accept" msgstr "Accepter" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "La dĆ©couverte a Ć©tĆ© dĆ©sactivĆ©e en raison du nombre de pĆ©riphĆ©riques sur ce rĆ©seau." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSL introuvable" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "Port dĆ©jĆ  utilisĆ©" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: la batterie est pleine" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "ComplĆØtement chargĆ©" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: la batterie est faible" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%dĀ %% restant" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Presse-papiers" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "Envoyer le presse-papiers" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "RĆ©cupĆ©rer le presse-papiers" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Trouver mon tĆ©lĆ©phone" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "PavĆ© tactile" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Clavier" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "Inconnu" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Annuler la notification" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Fermer la notification" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "RĆ©pondre Ć  la notification" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Active la notification" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Ɖchec du transfert" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Ɖchec d'envoi de « %s » vers %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping : %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "PrĆ©sentation" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "ExĆ©cuter des commandes" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Monter" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "DĆ©monter" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s a signalĆ© une erreur" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Partage" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s n'est pas autorisĆ© Ć  tĆ©lĆ©charger des fichiers" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "Transfert du fichier" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "RĆ©ception de « %s » depuis %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Transfert rĆ©ussi" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "ReƧu « %s » depuis %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Ouvrir le dossier" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Ouvrir le fichier" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Ɖchec de rĆ©ception de « %s » depuis %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Texte partagĆ© par %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Envoi de « %s » vers %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "« %s » envoyĆ© vers %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Envoyer des fichiers vers %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Ouvrir une fois fini" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Envoyer un lien vers %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Nouveau SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "RĆ©pondre au SMS" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "Partager le SMS" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Volume du systĆØme" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio introuvable" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Mettre l'appel en sourdine" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Contact inconnu" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Appel entrant" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Appel sortant" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Fax" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Professionnel" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Mobile" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Domicile" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Envoyer vers %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "ƀ l'instant" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Hier惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minute" msgstr[1] "%d minutes" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Message de groupe" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Vous: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "Et %d autre contact" msgstr[1] "Et %d autres" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "Le clavier distant sur %s n'est pas actif" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%dĀ %% (estimation en cours…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%dĀ %% (%d∶%02d jusqu'Ć  charge complĆØte)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%dĀ %% (%d∶%02d restant)" #: src/shell/notification.js:54 msgid "Reply" msgstr "RĆ©pondre" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Partagez des liens avec GSConnect, directement vers le navigateur ou par SMS." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Service indisponible" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Ouvrir dans le navigateur" gnome-shell-extension-gsconnect-50/po/fy-NL.po000066400000000000000000001026421421543444100214170ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-09 01:51\n" "Last-Translator: \n" "Language-Team: Frisian\n" "Language: fy_NL\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: fy-NL\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "KDE Connect implementaasje foar GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "GSConnect Team" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnect is in folsleine implementaasje fan KDE Connect, spesjaal foar GNOME Shell mei Nautilus, Chrome en Firefox integraasje. It KDE Connect team hat applikaasjes foar Linux, BSD, Android, Sailfish, macOS en Windows." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "Mei GSConnect kinst feilich ferbine mei mobiele apperaten en oare kompĆ»ters om:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Triemen, ferwiizingen en tekst te ferstjoeren" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Berjochten te ferstjoeren en binnen te krijen" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Klemboerd ynhĆ¢ld syngronisearje" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Kontakten syngronisearje" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Notifikaasjes te syngronisearjen" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Media spilers te bestjoeren" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "LĆ»dsynstellings fan it systeem te feroarjen" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Fan te foaren definiearre kommando's Ć»t te fieren" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "En meer…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect yn GNOME Shell" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Ferbine mei…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Ɣfbrekke" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Ferbine" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP Adres" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "Gjin kontakten" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Help" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Fier in telefoannĆ»mer of namme yn" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Oars" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "SMS Ferstjoere" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Ferstjoere" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Ferbining mei apperaat ferbrutsen" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Berjocht Ferstjoere" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Berjocht ynfiere" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "Berjocht Ynfier" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Fier in berjocht yn en druk op Enter om te ferstjoeren" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Berjochten" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Nij Petear" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Gjin Petearen" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Gjin petear selektearre" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Selektear of begjin in petear" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Kommando Feroarje" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Bewarje" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Namme" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Kommandorigel" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Útfierbere triem selektearje" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Iepenje" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Desktop" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "Kamera" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Klemboerd Syngronisaasje" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Mediaspilers" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "MĆ»s & Toetseboerd" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "LĆ»dsynstellings" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Triemen" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Triemen ƛntfange" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Triemen bewarje yn" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Diele" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Apperaat batterij" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Lege Batterij Notifikaasje" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Folslein Opladen Notifikaasje" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Systeem Batterij" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Statistiken Diele" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Baterij" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Kommandos" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Kommando Tafoegje" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Notifikaasjes Diele" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Diele Wannear Aktyf" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Applikaasjes" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Notifikaasjes" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Kontakten" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Ynkommende Tillefoantsjes" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "LĆ»dsynstellingen" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Media Skoftsje" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Útgeande Tillefoantsjes" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Mikrofoan bedimje" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Telefoan" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Aksje Fluchtoetsen" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Alles Werom Sette…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Ferkoartingen" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Plugins" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Eksperimenteel" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Apparaat Cache" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Cache leechje…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Efterhelle SMS Ć»ndersteuning" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "SFTP Automatysk ferbine" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "Avansearre" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Toetseboerd Fluchtoetsen" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "Apparaat Ynstellingen" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Keppelje" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "Apparaat is net keppele" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Do kinst dit apparaat konfigurearje foar it keppeljen" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Fersifering Ynformaasje" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "ƛntkeppelje" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Nei Apparaat" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Fan Apparaat" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Neat" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Werom Sette" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Omleech" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Bedimje" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Sette" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Druk op Esc om Ć“f te brekken as Backspace om de fluchtoets opnij yn te stellen." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Apparaat Namme" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "He_Rnimme" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Fernije" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "Mobiele Ynstellingen" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Tsjinst Menu" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "Apparaat Menu" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Apparaat Namme Feroarje" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Apparaten" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Om apparaten sykje…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Browser Add-Ons" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Ynskeakelje" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Dit apparaat is net sichtber foar net keppele apparaten" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "ƛntdekken Útskeakelje" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Werjaan Mode" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Paniel" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "BrĆ»kers menu" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Stipe logboek generearje" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "Oer GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Apparaat Selektearje" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Selektearje" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Gjin Apparaat FĆ»n" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Apparaat list" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Oanjaan" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Der is wat mislearre" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "GSConnect is tsjin in probleem oanrĆ»n. Jou dit graach oan mei alle informaasje dy't brĆ»kber wĆ©ze kin." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Technyske Details" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "Nei Mobiel Apparaat ferstjoere" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Mobile Apparaten" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Oansette" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "" msgstr[1] "" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Feroarje" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Fuortsmite" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Útskeakele" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "In nije fluchtoets ynfiere om %s te feroarjen" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s wurd ol brĆ»kt" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "In folsleine KDE Connect implementaasje foar GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "Tjipke van der Heide " #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "Debug berjochten wurde bewarre. Nim de nediche stappen om it probleem harrensels foardwaan te litten en besjoch don it loch." #: src/preferences/service.js:414 msgid "Review Log" msgstr "Loch Besjen" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Laptop" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Snoadtillefoan" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tablet" #: src/preferences/service.js:488 msgid "Television" msgstr "Telefyzje" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Net Keppele" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Ferbining ferbrutsen" #: src/preferences/service.js:518 msgid "Connected" msgstr "FerbĆ»n" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "Op in ferbining oan it wachtsjen…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "NĆ»mer Skilje" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Triem Ferstjoere" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Beskikbere apparaten sjen litte" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Olle apparaten sjen litte" #: src/service/daemon.js:369 msgid "Target Device" msgstr "Doel Apparaat" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Berjocht YnhĆ¢ld" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Notifikaasje Ferstjoere" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "Notifikaasje Applikaasje Namme" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Notifikaasje Haadtekst" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Notifikaasje Ć“fbylding" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "Notifikaasje ID" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Ɣfbylding" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Ɣfgean Litte" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Keppeling Diele" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Tekst Diele" #: src/service/daemon.js:528 msgid "Show release version" msgstr "Útjefte Ferzje Sjen Litte" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Net beskikber" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Keppelingsfersyk fan %s" #: src/service/device.js:800 msgid "Reject" msgstr "Ɣfslaan" #: src/service/device.js:805 msgid "Accept" msgstr "Tastean" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "Discovery is Ćŗtskeakele troch it oantal mobiele apparaten op dit netwurk." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSL net fĆ»n" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "Poarte wurd ol brĆ»kt" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: Batterij is fol" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "Foslein Opladen" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: Batterij is leech" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% te gean" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Klemboerd" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "Klemboerd Ferstjoere" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "Klemboerd Ophelje" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Fyn Myn Tillefoan" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "MĆ»smatte" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Toetseboerd" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "ƛnbekend" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Notifikaasje Ɣfbrekke" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Notifikaasje ƔfslĆ»te" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "Notifikaasje foar Antwurdzjen" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Notifikaasjes Ynskeakelje" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Oerdracht Mislearre" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Ferstjoeren fan \"%s\" nei %s mislearre" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "Presentaasje" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Komandos Útfiere" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Oankeppelje" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Loskeppelje" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Diele" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s mei gjin triemen uploade" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "Triemen Oersette" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "\"%s\" fan %s Ć»ntfong" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Oerset Slagge" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "\"%s\" fan %s Ć»ntfong" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Folder Iepenje" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Triem Iepenje" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Koe \"%s\" net fan %s binnen krije" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Tekst Dielt Troch %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "\"%s\" nei %s Oan't Stjoeren" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "ā€œ%sā€ nei %s ferstjoere" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Triemen nei %s ferstjoere" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Iepenje wannear klear" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Keppeling nei %s ferstjoere" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Nije SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "Op SMS Reagearje" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "SMS Diele" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Systeem LĆ»dsynstellings" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio net fĆ»n" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Tillefoantsje Bedimje" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "ƛnbekend Kontakt" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Binnenkommende Tillefoantsjes" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Útgeand Tillefoantsje" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Faks" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Wurk" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Mobyl" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "ThĆ»s" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Ferstjoer nei %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "No krekt" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Juster惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minĆŗt" msgstr[1] "%d minuten" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Berjochten Groepearje" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Do: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "En %d oar kontakt" msgstr[1] "En %d oare kontakten" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "Toetseboerd op Ć“fstĆ¢n op %s is net aktyf" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (Te gean…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d∶%02d Tot Fol)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d∶%02d Te Gean)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Reagearje" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Keppelingen mei GSConnect ferstjoere, direkt nei de browser of mei SMS." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Tsjinst ƛnbeskikber" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Yn Browser Iepenje" gnome-shell-extension-gsconnect-50/po/gl.po000066400000000000000000001032031421543444100210660ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-09 01:50\n" "Last-Translator: \n" "Language-Team: Galician\n" "Language: gl_ES\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: gl\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "Implementación do KDE Connect para GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "Equipo GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnect Ć© unha implementación completa de KDE Connect especialmente para o Shell de Gnome con integración con Nautilus, Chrome e Firefox. O equipo de KDE Connect ten aplicacións para Linux, BSD, Android, Sailfish, macOS e Windows." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "Con GSConnect pode conectar con seguranza con dispositivos móbiles e outros escritorio para:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Compartir ficheiros, ligazóns e texto" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Enviar e recibir mensaxes" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Sincronizar contido do portapapeis" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Sincronizar contactos" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Sincronizar notificacións" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Controlar reprodutores multimedia" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "Controla o volume do sistema" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Executar ordes predefinidas" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "E mĆ”is…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect na Shell de GNOME" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Conectar con…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Anular" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Conectar" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "Enderezo IP" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "Sen contactos" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Axuda" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Escriba o nĆŗmero de telĆ©fono ou nome" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Outro" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "Enviar SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Enviar" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "O dispositivo estĆ” desconectado" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Enviar mensaxe" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Escriba unha mensaxe" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "Entrada de mensaxe" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Escriba unha mensaxe e prema Intro para enviala" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Mensaxes" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Nova conversa" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Sen conversas" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Non se seleccionou conversa" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Seleccionar ou comezar conversa" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Editar a orde" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Gardar" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Nome" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "LiƱa de ordes" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Escoller un executĆ”bel" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Abrir" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Escritorio" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "CĆ”mara" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Sincronizar portapapeis" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Reprodutores multimedia" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Rato e teclado" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Control de volume" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Ficheiros" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Recibir ficheiros" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Gardar ficheiros en" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Compartindo" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "BaterĆ­a do dispositivo" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Notificación de baterĆ­a baixa" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Notificación de baterĆ­a a tope de carga" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "BaterĆ­a do sistema" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Compartir estatĆ­sticas" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "BaterĆ­a" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Ordes" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Engadir orde" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Compartir notificacións" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Compartir cando estea activo" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Aplicativos" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Notificacións" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Contactos" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Chamadas entrantes" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Volume" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Deter reprodución" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Chamadas saĆ­ntes" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Silenciar micrófono" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "TelefonĆ­a" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Atallos de acción" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Reiniciar todo…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Atallos" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Engadidos" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Experimental" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "CachĆ© do dispositivo" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Despexar a caché…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Compatibilidade con SMS herdados" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "Montado automĆ”tico SFTP" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "Avanzado" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Atallos de teclado" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "Configuración do dispositivo" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Enparellar" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "O dispositivo non estĆ” emparellado" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Pode configurar este dispositivo antes do emparellado" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Info de cifrado" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Desemparellar" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "A dispositivo" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Do dispositivo" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "NingĆŗn" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Restaurar" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Baixar" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Silencio" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Definir" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Premer Esc para anular ou Retroceso para redefinir o atallo de teclado." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Nome do dispositivo" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_Renomear" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Actualizar" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "Configuración móbil" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "MenĆŗ de servizo" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "MenĆŗ do dispositivo" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Editar o nome do dispositivo" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Dispositivos" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Buscando dispositivos…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Engadidos de navegador" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Activar" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Este dispositivo Ć© invisĆ­bel para dispositivos non emparellados" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Descuberta desactivada" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Modo de visualización" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Panel" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "MenĆŗ do usuario" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Xerar o rexistro para asistencia" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "Verbo de GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Seleccionar un dispositivo" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Seleccionar" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Non se atopou o dispositivo" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Lista do dispositivo" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Informar" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Algo foi mal" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "GSConnect atopou un erro inesperado. Informe do problema e inclĆŗa toda información que poida ser de axuda." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Detalles tĆ©cnicos" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "Enviar a dispositivo móbil" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Dispositivos móbiles" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Acender" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d Conectado" msgstr[1] "%d Conectados" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Editar" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Retirar" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Desactivado" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Escribir un novo atallo para cambiar%s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s xa estĆ” en uso" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Unha implementación completa de KDE Connect para GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "tradutor" #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "Estanse a rexistrar as mensaxes de depuración. Faga o necesario para reproducir o problema e logo revise o rexistro." #: src/preferences/service.js:414 msgid "Review Log" msgstr "Revisar o rexistro" #: src/preferences/service.js:482 msgid "Laptop" msgstr "PortĆ”til" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Móbil" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tableta" #: src/preferences/service.js:488 msgid "Television" msgstr "Televisión" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Desemparellado" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Desconectado" #: src/preferences/service.js:518 msgid "Connected" msgstr "Conectado" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "Agardar polo servizo…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "Prema para obter axuda de solucións" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "Premer para ter mĆ”is información" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "NĆŗmero en dial" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Compartir ficheiro" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Lista de dispositivos dispoƱƭbeis" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Listar todos os dispositivos" #: src/service/daemon.js:369 msgid "Target Device" msgstr "Dispositivo de destino" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Corpo da mensaxe" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Enviar a notificación" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "Nome da app notificada" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Corpo da notificación" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Icona da notificación" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "ID de notificación" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Foto" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Timbre" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Compartir ligazón" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Compartir texto" #: src/service/daemon.js:528 msgid "Show release version" msgstr "Amosar a versión da edición" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Non dispoƱƭbel" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Dispositivo bluetooth en %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "Dactilograma %s:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Petición de emparellado desde %s" #: src/service/device.js:800 msgid "Reject" msgstr "Rexeitar" #: src/service/device.js:805 msgid "Accept" msgstr "Aceptar" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "A descuberta desactivouse debido ao nĆŗmero de dispositivos nesta rede." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "Non se atopou o OpenSSL" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "O porto xa estĆ” sendo utilizado" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: A baterĆ­a estĆ” chea" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "Carga completa" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: A baterĆ­a estĆ” baixa" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "Queda o %d%%" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Portapapeis" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "Entregar do portapapeis" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "Recoller no portapapeis" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Atopar o meu telĆ©fono" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "Ɓrea de rato" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Teclado" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "DescoƱecido" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Anular a notificación" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Pechar a notificación" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "Responder a notificación" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Activar a notificación" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Fallou a transferencia" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Fallou o envĆ­o de \"%s\" a %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "Presentación" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Executar ordes" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Montar" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Desmontar" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Compartir" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s non permite cargar ficheiros" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "Transferindo o ficheiro" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "Recibindo \"%s\" de %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Transferencia correcta" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "RecibĆ­ronse \"%s\" desde %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Abrir cartafol" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Abrir ficheiro" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Fallou a recepción de \"%s\" desde %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Texto compartido por %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Enviando \"%s\" a %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "Enviar \"%s\" a %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Enviar ficheiros a %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Abrir cando estea feito" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Enviar unha ligazón a %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Novo SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "Responder a SMS" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "Compartir SMS" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Volume do sistema" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "Non se atopou PulseAudio" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Silenciar chamada" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Contacto descoƱecido" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Chamada entrante" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Chamada en curso" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Fax" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Traballo" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Móbil" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Casa" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Enviar a %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Agora mesmo" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Onte惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minuto" msgstr[1] "%d minutos" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Mensaxe de grupo" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Vostede: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "E %d outro contacto" msgstr[1] "E %d outros" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "O teclado remoto de %s non estĆ” activo" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (EstĆ­mase…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d:%02d Ata completar)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d:%02d Restante)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Responder" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Compartir ligazóns con GSConnect, directamente co navegador ou por SMS." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Servizo non dispoƱƭbel" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Abrir no navegador" gnome-shell-extension-gsconnect-50/po/hu.po000066400000000000000000001056111421543444100211050ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2022-03-10 21:51\n" "Last-Translator: \n" "Language-Team: Hungarian\n" "Language: hu_HU\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: hu\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "KDE Connect implementĆ”ció GNOME-hoz" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "A GSConnect csapata" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "A GSConnect egy teljes KDE Connect implementĆ”ció főkĆ©nt a GNOME Shell-hez, Nautilus, Chrome Ć©s Firefox integrĆ”cióval. A KDE Connect csapatĆ”nak alkalmazĆ”sai elĆ©rhetőek Linux, BSD, Android, Sailfish, macOS Ć©s Windows rendszereken." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Üzenetek küldĆ©se Ć©s fogadĆ”sa" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "VĆ”gólap tartalmĆ”nak szinkronizĆ”lĆ”sa" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "NĆ©vjegyek szinkronizĆ”lĆ”sa" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "ƉrtesĆ­tĆ©sek szinkronizĆ”lĆ”sa" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "MĆ©dialejĆ”tszók irĆ”nyĆ­tĆ”sa" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "Rendszerhangerő vezĆ©rlĆ©se" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Előre megadott parancsok vĆ©grehajtĆ”sa" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "Ɖs mĆ©g sok mĆ”s…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "KapcsolódĆ”s egy eszkƶzhƶz…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "MĆ©gse" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "CsatlakozĆ”s" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP-cĆ­m" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "Nincsenek nĆ©vjegyek" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "SĆŗgó" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Adjon meg egy szĆ”mot vagy nevet" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "EgyĆ©b" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "SMS küldĆ©se" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "KüldĆ©s" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Az eszkƶz nincs csatlakoztatva" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Üzenet küldĆ©se" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "ƍrja be az üzenetet" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "ÜzenetküldĆ©s" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Új beszĆ©lgetĆ©s" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Nincsenek beszĆ©lgetĆ©sek" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Nincs kivĆ”lasztva beszĆ©lgetĆ©s" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "IndĆ­tson, vagy vĆ”lasszon ki egy beszĆ©lgetĆ©st" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Parancs szerkesztĆ©se" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "MentĆ©s" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "NĆ©v" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Parancssor" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "VĆ”lassz egy programot" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "MegnyitĆ”s" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Asztali gĆ©p" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "FĆ©nykĆ©pező" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "VĆ”gólap szinkronizĆ”lĆ”s" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "MĆ©dialejĆ”tszók" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "EgĆ©r Ć©s billentyűzet" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "HangerőszabĆ”lyzó" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "FĆ”jlok" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "FĆ”jlok fogadĆ”sa" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "FĆ”jlok mentĆ©se ide" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "MegosztĆ”s" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Eszkƶz akkumulĆ”tor" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Alacsony tƶltƶttsĆ©g Ć©rtesĆ­tĆ©s" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "Szabadon Ć”llĆ­tható tƶltƶttsĆ©g Ć©rtesĆ­tĆ©s" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Teljes tƶltƶttsĆ©g Ć©rtesĆ­tĆ©s" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Rendszer akkumulĆ”tor" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "StatisztikĆ”k megosztĆ”sa" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "AkkumulĆ”tor" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Parancsok" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Parancs hozzĆ”adĆ”sa" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "ƉrtesĆ­tĆ©sek megosztĆ”sa" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "MegosztĆ”s Amikor AktĆ­v" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "AlkalmazĆ”sok" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "ƉrtesĆ­tĆ©sek" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "NĆ©vjegyek" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Bejƶvő hĆ­vĆ”sok" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Hangerő" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "MĆ©dia szüneteltetĆ©se" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Kimenő hĆ­vĆ”sok" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Mikrofon nĆ©mĆ­tĆ”sa" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "TelefonĆ”lĆ”s" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Művelet gyorsbillentyűk" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Ɩsszes visszaĆ”llĆ­tĆ”sa…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Gyorsbillentyűk" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "BővĆ­tmĆ©nyek" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "KĆ­sĆ©rleti" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Eszkƶz gyorsĆ­tótĆ”r" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "GyorsĆ­tótĆ”r ürĆ­tĆ©se…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "RĆ©gi fajta SMS tĆ”mogatĆ”s" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "SFPT automatikus csatolĆ”s" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "Haladó" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Gyorsbillentyűk" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "Eszkƶz beĆ”llĆ­tĆ”sai" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "PĆ”rosĆ­tĆ”s" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "Az eszkƶz nincs pĆ”rosĆ­tva" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "PĆ”rosĆ­tĆ”s előtt testre szabhatja az eszkƶzt" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "TitkosĆ­tĆ”si informĆ”ció" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "PĆ”rosĆ­tĆ”s megszűntetĆ©se" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Eszkƶzre" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Eszkƶzről" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Semmi" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "VisszaĆ”llĆ­tĆ”s" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "HalkĆ­tĆ”s" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "NĆ©mĆ­tĆ”s" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "KivĆ”lasztĆ”s" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "A megszakĆ­tĆ”shoz nyomjon Esc gombot, vagy Backspace-t a gyorsbillentyű visszaĆ”llĆ­tĆ”sĆ”hoz." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Eszkƶz neve" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_ƁtnevezĆ©s" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "FrissĆ­tĆ©s" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "Mobil beĆ”llĆ­tĆ”sok" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "Eszkƶzmenü" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Eszkƶz nevĆ©nek szerkesztĆ©se" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Eszkƶzƶk" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Eszkƶzƶk keresĆ©se…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "BƶngĆ©sző bővĆ­tmĆ©nyek" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "EngedĆ©lyez" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Ez az eszkƶz nem lĆ”tható a pĆ”rosĆ­tatlan eszkƶzƶk szĆ”mĆ”ra" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "FelderĆ­tĆ©s letiltva" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "MegjelenĆ­tĆ©si mód" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Panel" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "FelhasznĆ”lói menü" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "NaplófĆ”jl kĆ©szĆ­tĆ©se termĆ©ktĆ”mogatĆ”shoz" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "A GSConnect nĆ©vjegye" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Eszkƶz kivĆ”lasztĆ”sa" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "KivĆ”lasztĆ”s" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Nem talĆ”lható eszkƶz" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Eszkƶzlista" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "JelentĆ©s" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Valami nem stimmel" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "A GSConnect egy vĆ”ratlan hibĆ”ba ütkƶzƶtt. KĆ©rlek jelentsd a hibĆ”t, Ć©s csatolj minden informĆ”ciót, ami segĆ­thet." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Technikai rĆ©szletek" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "KüldĆ©s mobil eszkƶzre" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Mobil eszkƶzƶk" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "BekapcsolĆ”s" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "Egy eszkƶz csatlakoztatva" msgstr[1] "%d eszkƶz csatlakoztatva" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "KikapcsolĆ”s" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "SzerkesztĆ©s" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "EltĆ”volĆ­tĆ”s" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Letiltva" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "%s megvĆ”ltoztatĆ”sĆ”hoz adjon meg egy Ćŗj gyorsbillentyűt" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s mĆ”r hasznĆ”latban van" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Teljes KDE Connect implementĆ”ció GNOME-hoz" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "BĆ”thory PĆ©ter " #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "A hibakeresĆ©si üzenetek naplózĆ”sra kerülnek. Tegye meg a hiba reprodukĆ”lĆ”sĆ”hoz szüksĆ©ges lĆ©pĆ©st, majd nĆ©zze Ć”t a naplót." #: src/preferences/service.js:414 msgid "Review Log" msgstr "Napló Ć”tnĆ©zĆ©se" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Laptop" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Okostelefon" #: src/preferences/service.js:486 msgid "Tablet" msgstr "TĆ”blagĆ©p" #: src/preferences/service.js:488 msgid "Television" msgstr "TV" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Nincs pĆ”rosĆ­tva" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Nincs csatlakoztatva" #: src/preferences/service.js:518 msgid "Connected" msgstr "Csatlakoztatva" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "VĆ”rakozĆ”s a szolgĆ”ltatĆ”sra…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "Kattintson ide, hogy segĆ­tsĆ©get kapjon a hibaelhĆ”rĆ­tĆ”shoz" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "TovĆ”bbi informĆ”cióért kattintson ide" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "TelefonszĆ”m hĆ­vĆ”sa" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "FĆ”jl megosztĆ”sa" #: src/service/daemon.js:351 msgid "List available devices" msgstr "ElĆ©rhető eszkƶzƶk listĆ”zĆ”sa" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Ɩsszes eszkƶz listĆ”zĆ”sa" #: src/service/daemon.js:369 msgid "Target Device" msgstr "CĆ©l eszkƶz" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Üzenet szƶvege" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "ƉrtesĆ­tĆ©s küldĆ©se" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "ƉrtesĆ­tĆ©s alkalmazĆ”snĆ©v" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "ƉrtesĆ­tĆ©s tƶrzs" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "ƉrtesĆ­tĆ©s ikon" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "ƉrtesĆ­tĆ©s ID" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Fotó" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "CsƶrgetĆ©s" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "HivatkozĆ”s megosztĆ”sa" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Szƶveg megosztĆ”sa" #: src/service/daemon.js:528 msgid "Show release version" msgstr "KiadĆ”s verziószĆ”m megjelenĆ­tĆ©se" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Nem Ć©rhető el" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Bluetooth eszkƶz itt: %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "%s ujjlenyomat:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "PĆ”rosĆ­tĆ”si kĆ©relem innen: %s" #: src/service/device.js:800 msgid "Reject" msgstr "ElutasĆ­tĆ”s" #: src/service/device.js:805 msgid "Accept" msgstr "ElfogadĆ”s" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "A felderĆ­tĆ©s le lett tiltva a hĆ”lózaton lĆ©vő eszkƶzƶk szĆ”ma miatt." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "Az OpenSSL nem talĆ”lható" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "A port mĆ”r hasznĆ”latban van" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "AkkumulĆ”toradatok küldĆ©se Ć©s fogadĆ”sa" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: AkkumulĆ”tor feltƶltve" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "Teljesen feltƶltve" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "%s: az akkumulĆ”tor elĆ©rte a megadott tƶltƶttsĆ©gi szintet" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: alacsony tƶltƶttsĆ©g" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% van hĆ”tra" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "VĆ”gólap" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "VĆ”gólap tartalmĆ”nak megosztĆ”sa" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "VĆ”gólap küldĆ©s" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "VĆ”gólap fogadĆ”s" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "A pĆ”rosĆ­tott eszkƶz nĆ©vjegyeinek elĆ©rĆ©se" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Keresd meg a telefonom" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "A pĆ”rosĆ­tott eszkƶz megcsƶrgetĆ©se" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "EgĆ©rpad" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "A pĆ”rosĆ­tott eszkƶz egĆ©rkĆ©nt Ć©s billentyűzetkĆ©nt való hasznĆ”latĆ”nak egedĆ©lyezĆ©se" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Billentyűzet" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "Ismeretlen" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "ƉrtesĆ­tĆ©sek megosztĆ”sa a pĆ”rosĆ­tott eszkƶzzel" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "ƉrtesĆ­tĆ©s megszakĆ­tĆ”sa" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "ƉrtesĆ­tĆ©s bezĆ”rĆ”sa" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "ƉrtesĆ­tĆ©sek küldĆ©se" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "ƉrtesĆ­tĆ©s aktivĆ”lĆ”sa" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Sikertelen Ć”tvitel" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "ā€ž%sā€ küldĆ©se %s eszkƶzre nem sikerült" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "PrezentĆ”ció" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Parancsok futtatĆ”sa" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "Futtass parancsokat egy pĆ”rosĆ­tott eszkƶzƶn, vagy engedĆ©lyzed, hogy előre megadott parancsokat futtassanak ezen a eszkƶzƶn" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "A pĆ”rosĆ­tott eszkƶz fĆ”jlrendszerĆ©nek bƶngĆ©szĆ©se" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "CsatolĆ”s" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "LevĆ”lasztĆ”s" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s hibĆ”t jelzett" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "MegosztĆ”s" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "FĆ”jlok Ć©s URL-ek megosztĆ”sa eszkƶzƶk kƶzƶtt" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "% szĆ”mĆ”ra nem engedĆ©lyezett a fĆ”jlok feltƶltĆ©se" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "FĆ”jl Ć”tvitele" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "ā€ž%sā€ fogadĆ”sa innen: %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Sikeres Ć”tvitel" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "ā€ž%sā€ fogadva innen: %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "KƶnyvtĆ”r megnyitĆ”sa" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "FĆ”jl megnyitĆ”sa" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "ā€ž%sā€ fogadĆ”sa %s eszkƶzről nem sikerült" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "%s Ć”ltal megosztott szƶveg" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "ā€ž%sā€ küldĆ©se ide: %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "ā€ž%sā€ elküldve ide: %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "FĆ”jlok küldĆ©se ide: %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "MegnyitĆ”s ha kĆ©sz" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "HivatkozĆ”s küldĆ©se ide: %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "ƉrtesĆ­tĆ©s Ćŗj SMS-ekről, Ć©s a pĆ”rosĆ­tott eszkƶz SMS-einek olvasĆ”sa Ć©s SMS-ek küldĆ©se" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Új SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "SMS vĆ”lasz" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "SMS megosztĆ”sa" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Rendszerhangerő" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "A rendszerhangerő Ć”llĆ­tĆ”sĆ”nak egedĆ©lyezĆ©se a pĆ”rosĆ­tott eszkƶz szĆ”mĆ”ra" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio nem talĆ”lható" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "ƉrtesĆ­tĆ©s a hĆ­vĆ”sokról Ć©s a rendszerhangerő Ć”llĆ­tĆ”sa csƶrgĆ©s/kimenő hĆ­vĆ”sok kƶzben" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "HĆ­vĆ”s nĆ©mĆ­tĆ”sa" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Ismeretlen nĆ©vjegy" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Bejƶvő hĆ­vĆ”s" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Kimenő hĆ­vĆ”s" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Fax" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Munkahelyi" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Mobil" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Otthoni" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "KüldĆ©s neki: %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Ɖpp most" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Tegnap惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d perc" msgstr[1] "%d perc" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Csoportos üzenet" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Te: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "Ɖs %d mĆ”sik nĆ©vjegy" msgstr[1] "Ɖs tovĆ”bbi %d" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "A tĆ”voli billentyűzet %s eszkƶzƶn nem aktĆ­v" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (becslĆ©s…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d∶%02d a feltƶltĆ©sig)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d∶%02d van hĆ”tra)" #: src/shell/notification.js:54 msgid "Reply" msgstr "VĆ”lasz" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "HivatkozĆ”sok megosztĆ”sa GSConnecttel, kƶzvetlenül a bƶngĆ©szőből vagy SMS-ben." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "A szolgĆ”ltatĆ”s nem Ć©rhető el" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "MegnyitĆ”s bƶngĆ©szőben" gnome-shell-extension-gsconnect-50/po/id-ID.po000066400000000000000000000772221421543444100213650ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-09 01:50\n" "Last-Translator: \n" "Language-Team: Indonesian\n" "Language: id_ID\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: id\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "Implementasi KDE Connect untuk GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "Tim GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Membagikan berkas, tautan, dan teks" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Mengirim serta menerima pesan" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Menyinkronkan isi papan klip" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Menyinkronkan kontak" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Menyinkronkan notifikasi" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "Mengendalikan volume sistem" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "Dan banyak lagi…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect di GNOME Shell" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Hubungkan dengan…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Batal" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Hubungkan" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "Alamat IP" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Bantuan" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Ketik nomor telepon atau nama" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Lainnya" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "Kirim SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Kirim" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Perangkat terputus" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Kirim Pesan" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Ketik pesan" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Ketik pesan dan tekan Enter untuk mengirim" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Perpesanan" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Percakapan Baru" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Tidak Ada Percakapan" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Tidak ada percakapan yang dipilih" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Pilih atau mulai sebuah percakapan" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Sunting Perintah" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Simpan" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Nama" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Buka" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Desktop" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "Kamera" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Sinkronisasi Papan Klip" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Pemutar Media" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Tetikus & Papan Ketik" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Berkas-berkas" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Terima berkas-berkas" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Simpan berkas-berkas di" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Berbagi" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Baterai Perangkat" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Notifikasi Baterai Lemah" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Notifikasi Baterai Terisi Penuh" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Baterai Sistem" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Bagikan Statistik" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Baterai" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Tambahkan Perintah" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Bagikan Notifikasi" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Aplikasi-aplikasi" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Notifikasi" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Kontak" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Panggilan Masuk" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Volume" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Panggilan Aktif" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Bisukan Mikrofon" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Telepon" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Pintasan Tindakan" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Pintasan" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Plugin-plugin" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Eksperimental" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Cache Perangkat" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Hapus Cache…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "Lanjutan" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Pintasan Papan Ketik" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "Pengaturan Perangkat" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Hubungkan" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "Perangkat tidak terhubung" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Informasi Enkripsi" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Putuskan" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Ke Perangkat" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Dari Perangkat" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Tidak Ada" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Pulihkan" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Kecilkan" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Bisukan" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "" #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Nama Perangkat" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Muat ulang" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Menu Layanan" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "Menu Perangkat" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Sunting Nama Perangkat" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Perangkat-perangkat" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Mencari perangkat…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Aktifkan" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Panel" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "Tentang GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Pilih Perangkat" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Pilih" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Tidak Ada Perangkat Yang Ditemukan" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Daftar Perangkat" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Laporkan" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Ada yang salah" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "GSConnect mengalami kesalahan yang tak terduga. Harap laporkan masalahnya dan sertakan informasi yang mungkin membantu." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Keterangan Teknis" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Aktifkan" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d terhubung" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Nonaktifkan" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Sunting" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Hapus" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Dinonaktifkan" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s sudah digunakan" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Implementasi lengkap KDE Connect untuk GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "@liimee" #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "" #: src/preferences/service.js:414 msgid "Review Log" msgstr "" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Laptop" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Ponsel" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tablet" #: src/preferences/service.js:488 msgid "Television" msgstr "Televisi" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Tidak terhubung" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Terputus" #: src/preferences/service.js:518 msgid "Connected" msgstr "Terhubung" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "Menunggu layanan…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "Klik untuk informasi lebih lanjut" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Bagikan Berkas" #: src/service/daemon.js:351 msgid "List available devices" msgstr "" #: src/service/daemon.js:360 msgid "List all devices" msgstr "" #: src/service/daemon.js:369 msgid "Target Device" msgstr "" #: src/service/daemon.js:411 msgid "Message Body" msgstr "" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Kirim Notifikasi" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Isi Notifikasi" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Ikon Notifikasi" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "ID Notifikasi" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Foto" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Bagikan Tautan" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Bagikan Teks" #: src/service/daemon.js:528 msgid "Show release version" msgstr "" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Tidak tersedia" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "" #: src/service/device.js:800 msgid "Reject" msgstr "Tolak" #: src/service/device.js:805 msgid "Accept" msgstr "Terima" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "" #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSL tidak ditemukan" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "Port sudah digunakan" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: Baterai penuh" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "Terisi Penuh" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "%d%% Terisi" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: Baterai lemah" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% tersisa" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Papan Klip" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "Bagikan isi papan klip" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Temukan Ponselku" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Papan ketik" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "Minta perangkat yang terhubung untuk mengambil foto dan mengirimkannya ke PC ini" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Gagal mengirim ā€œ%sā€ ke %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "Kirim dan terima ping" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s melaporkan sebuah kesalahan" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Bagikan" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s tidak diizinkan untuk mengunggah berkas" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "Menerima ā€œ%sā€ dari %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "ā€œ%sā€ dari %s diterima" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Buka Folder" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Buka Berkas" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Gagal menerima ā€œ%sā€ dari %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Teks Dibagikan Oleh %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Mengirim ā€œ%sā€ ke %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "ā€œ%sā€ dikirim ke %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Kirim berkas-berkas ke %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Buka saat selesai" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Kirim tautan ke %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "Kirim dan baca SMS di perangkat yang terhubung dan dapatkan notifikasi ketika menerima SMS baru" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "SMS Baru (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "Balas SMS" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Volume Sistem" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio tidak ditemukan" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Panggilan masuk" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Panggilan aktif" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Faks" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Kantor" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Seluler" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Rumah" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Kirim ke %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Baru saja" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Kemarin • %s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d menit" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Anda: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "Dan %d kontak lain" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (Memperkirakan…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d:%02d Sampai Penuh)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d:%02d Tersisa)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Balas" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "" #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Layanan Tidak Tersedia" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Buka di Browser" gnome-shell-extension-gsconnect-50/po/it.po000066400000000000000000001050261421543444100211050ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-09 01:51\n" "Last-Translator: \n" "Language-Team: Italian\n" "Language: it_IT\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: it\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "Implementazione di KDE Connect per GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "Team GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnect ĆØ un'implementazione completa di KDE Connect per GNOME Shell, integrata con Nautilus, Chrome e Firefox. Il team di GSConnect produce applicazioni per Linux, BSD, Android, Sailfish, macOS e Windows." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "Con GSConnect ĆØ possibile connettersi in sicurezza a dispositivi mobili e altri computer per:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Condividere file, link e testi" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Inviare e ricevere messaggi" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Sincronizzare il contenuto degli appunti" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Sincronizzare i contatti" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Sincronizzare le notifiche" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Controllare i contenuti multimediali" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "Controllare il volume di sistema" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Eseguire comandi predefiniti" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "E altro…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect su GNOME Shell" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Connetti a…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Annulla" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Connetti" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "Indirizzo IP" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "Nessun contatto" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Aiuto" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Digita un numero di telefono o un nome" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Altro" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "Invia SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Invia" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Il dispositivo ĆØ disconnesso" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Invia un messaggio" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Digita un messaggio" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "Campo Messaggio" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Digitare un messaggio e premere Invio per inviarlo" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Messaggi" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Nuova conversazione" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Nessuna conversazione" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Nessuna conversazione selezionata" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Seleziona o inizia una conversazione" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Comando Modifica" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Salva" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Nome" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Linea di comando" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Seleziona un eseguibile" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Apri" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Desktop" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "Fotocamera" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Sincronizzazione appunti" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Riproduzione multimediale" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Mouse e tastiera" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Controllo volume" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "File" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Ricezione file" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Salva i file su" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Condivisione" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Batteria del dispositivo" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Notifica di batteria scarica" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "Notificare il raggiungimento del livello personalizzato di carica" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Notifica di ricarica completa" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Batteria di sistema" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Condivisione statistiche" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Batteria" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Comandi" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Aggiungere comando" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Condividi notifiche" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Condividi quando attivo" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Applicazioni" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Notifiche" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Contatti" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Chiamate in entrata" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Volume" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Metti in pausa il media" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Chiamate in uscita" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Disattiva il microfono" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Telefonia" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Scorciatoie azioni" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Ripristina tutte…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Scorciatoie" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Plugin" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Sperimentale" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Cache del dispositivo" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Cancella cache…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Supporto SMS legacy" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "Montaggio automatico SFTP" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "Avanzate" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Scorciatoie da tastiera" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "Impostazioni dispositivo" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Accoppia" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "Il dispositivo ĆØ disaccoppiato" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "ƈ possibile configurare questo dispositivo prima dell'accoppiamento" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Informazioni di criptazione" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Disaccoppia" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Al dispositivo" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Dal dispositivo" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Niente" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Ripristina" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Riduci" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Silenzia" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Imposta" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Premi ESC per annullare o Backspace per ripristinare la scorciatoia." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Nome del dispositivo" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_Rinomina" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Aggiorna" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "Impostazioni dispositivi" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Menu Servizio" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "Menu Dispositivo" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Modifica il nome del dispositivo" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Dispositivi" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Ricerca dei dispositivi…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Estensioni browser" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Abilita" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Questo dispositivo ĆØ invisibile ai dispositivi disaccoppiati" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Ricerca disattiva" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "ModalitĆ  di visualizzazione" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Pannello" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "Menu utente" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Genera log di supporto" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "Informazioni su GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Seleziona un dispositivo" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Seleziona" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Dispositivo non trovato" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Elenco dispositivi" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Segnalazione" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Qualcosa ĆØ andato storto" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "GSConnet ha riscontrato un errore imprevisto. Segnalare il problema e includere le informazioni necessarie." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Dettagli tecnici" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "Invia al dispositivo mobile" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Dispositivi mobili" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Attiva" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d connesso" msgstr[1] "%d connessi" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Disattiva" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Modifica" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Rimuovi" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Disabilitato" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Inserisci una nuova scorciatoia per %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s giĆ  in uso" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Un'implementazione completa di KDE Connect per GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "Jimmy Scionti " #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "I messaggi di debug vengono registrati. Adotta tutte le misure necessarie per riprodurre un problema, quindi rivedi il registro." #: src/preferences/service.js:414 msgid "Review Log" msgstr "Log delle Revisioni" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Laptop" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Smartphone" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tablet" #: src/preferences/service.js:488 msgid "Television" msgstr "Televisione" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Disaccoppiato" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Disconnesso" #: src/preferences/service.js:518 msgid "Connected" msgstr "Connesso" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "In attesa del servizio…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "Click per la risoluzione del problema" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "Click per altre informazioni" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Componi numero" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Invia file" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Elenco dei dispositivi disponibili" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Elenco di tutti i dispositivi" #: src/service/daemon.js:369 msgid "Target Device" msgstr "Dispositivo di destinazione" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Corpo del messaggio" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Invia notifica" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "Nome app di notifica" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Corpo della notifica" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Icona di notifica" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "ID della notifica" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Foto" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Squilla" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Condividi collegamento" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Invia testo" #: src/service/daemon.js:528 msgid "Show release version" msgstr "Mostrare versione di rilascio" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Non disponibile" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Dispositivo Bluetooth a %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "%s fingerprint:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Richiesta di accoppiamento da %s" #: src/service/device.js:800 msgid "Reject" msgstr "Rifiuta" #: src/service/device.js:805 msgid "Accept" msgstr "Accetta" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "La ricerca ĆØ stata disattivata a causa dell'elevato numero di dispositivi nella rete." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSL non trovato" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "Porta giĆ  in uso" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "Scambiare informazioni sulla batteria" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: la batteria ĆØ carica" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "Completamente carica" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "%s: la batteria ha raggiunto il livello di carica impostato" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "%d%% carico" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: batteria quasi scarica" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% rimanenti" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Appunti" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "Condividere il contenuto degli appunti" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "Invia appunti" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "Ricevi appunti" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "Accedere ai contatti del dispositivo" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Trova il mio telefono" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "Fare squillare il dispositivo" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "Mousepad" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "Permette di usare il dispositivo come un mouse e una tastiera" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Tastiera" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "Controllo di riproduzione multimediale bidirezionale" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "Sconosciuto" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "Condividere le notifiche con il dispositivo" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Cancella notifica" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Chiudi notifica" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "Rispondi alla notifica" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Attivare le notifiche" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "Richiedere al dispositivo di scattare una foto e trasferirla su questo PC" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Trasferimento fallito" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Invio fallito di \"%s\" a %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "Inviare e ricevere ping" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "Presentazione" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "Usa il dispositivo come presentatore" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Esegui comandi" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "Eseguire comandi sul dispositivo o lasciare che il dispositivo esegua comandi predefiniti su questo PC" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "Sfogliare il file system del dispositivo" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Monta" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Smonta" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s ha segnalato un errore" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Invia file" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "Condividere file e URL tra dispositivi" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s non può caricare file" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "Trasferimento file" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "Ricezione in corso di \"%s\" da %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Trasferimento riuscito" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "Ricevuto \"%s\" da %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Apri cartella" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Apri file" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Ricezione fallita di \"%s\" da %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Testo condiviso da %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Invio in corso di \"%s\" a %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "\"%s\" inviato a %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Invia file a %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Apri al termine" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Invia collegamento a %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "Inviare e leggere SMS del dispositivo e ricevere le notifiche per nuovi SMS" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Nuovo SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "Rispondi all'SMS" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "Condividi SMS" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Volume di sistema" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "Permettere al dispositivo di controllare il volume del sistema" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio non trovato" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "Ricevere notifiche sulle telefonate e livellare il volume durante le telefonate" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Silenzia la chiamata in arrivo" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Contatto sconosciuto" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Chiamata in arrivo" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Chiamata in corso" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Fax" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Lavoro" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Mobile" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Casa" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Invia a %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Proprio ora" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Ieri惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minuto" msgstr[1] "%d minuti" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Messaggio di gruppo" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Tu: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "E %d altro contatto" msgstr[1] "E altri %d" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "La tastiera remota su %s non ĆØ attiva" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (Stima…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d∶%02d Al completamento)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d∶%02d Rimanenti)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Rispondi" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Condividi collegamenti con GSConnect, direttamente nel browser o via SMS." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Servizio non disponibile" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Apri nel browser" gnome-shell-extension-gsconnect-50/po/ko-KR.po000066400000000000000000001023721421543444100214150ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-09 01:51\n" "Last-Translator: \n" "Language-Team: Korean\n" "Language: ko_KR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: ko\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "ź·øė†ˆģ„ ģœ„ķ•œ KDE Connectģ˜ ķ˜øķ™˜ 기늄" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "GSConnect ķŒ€" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnectėŠ” KDE Connect넼 ź·øė†ˆ ė°ģŠ¤ķ¬ķƒ‘ ķ™˜ź²½ģ—ģ„œ ģ™„ģ „ķžˆ ģ‚¬ģš©ķ•  수 ģžˆėŠ” ė„źµ¬ģž…ė‹ˆė‹¤. ė¦¬ėˆ…ģŠ¤, BSD, ģ•ˆė“œė”œģ“ė“œ, Sailfish, macOS, Windows용 KDE Connect ģ•±ģ„ ė°›ģ„ 수 ģžˆģŠµė‹ˆė‹¤." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "GSConnect딜 ėŖØė°”ģ¼ 기기와 다넸 ė°ģŠ¤ķ¬ķƒ‘ģ„ ģ•ˆģ „ķ•˜ź²Œ ģ—°ź²°ķ•˜źø°:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "ķŒŒģ¼ź³¼ 링크, ķ…ģŠ¤ķŠø ė“±ģ„ ź³µģœ ķ•©ė‹ˆė‹¤" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "ė©”ģ‹œģ§€ė„¼ 주고 받기" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "ķ“ė¦½ė³“ė“œ ė‚“ģ—­ ė™źø°ķ™”" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "ģ—°ė½ģ²˜ ė™źø°ķ™”" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "ģ•Œė¦¼ ė™źø°ķ™”" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "미디얓 ķ”Œė ˆģ“ģ–“ ģ œģ–“" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "ģ‹œģŠ¤ķ…œ 볼넨 ģ œģ–“" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "ģ •ģ˜ėœ ėŖ…ė ¹ 실행" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "ė” 볓기…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GNOME Shellģ˜ GSConnect" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "ģ—°ź²°" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "ģ·Øģ†Œ" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "ģ—°ź²°" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP ģ£¼ģ†Œ" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "ģ—°ė½ģ²˜ ģ—†ģŒ" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "ė„ģ›€ė§" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "ģ „ķ™”ė²ˆķ˜øė‚˜ ģ“ė¦„ģ„ ģž…ė „" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "źø°ķƒ€" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "ė©”ģ‹œģ§€ 볓낓기" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "볓낓기" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "ė””ė°”ģ“ģŠ¤ģ˜ ģ—°ź²°ģ“ ėŠź²¼ģŠµė‹ˆė‹¤" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "ė©”ģ‹œģ§€ 볓낓기" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "ė©”ģ‹œģ§€ ģž…ė „" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "ė©”ģ‹œģ§€ ėŖ©ė”" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "ė©”ģ‹œģ§€ė„¼ ģž…ė „ķ•œ 후 ģ „ģ†”ķ•˜ė ¤ė©“ Enter 키넼 ėˆ„ė„“ģ‹­ģ‹œģ˜¤" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "ė©”ģ‹œģ§€" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "새 ėŒ€ķ™”" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "ėŒ€ķ™” ģ—†ģŒ" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "ģ„ ķƒėœ ėŒ€ķ™” ģ—†ģŒ" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "ėŒ€ķ™”ė„¼ ģ„ ķƒķ•˜ź±°ė‚˜ ģ‹œģž‘" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "ėŖ…ė ¹ ķŽøģ§‘" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "ģ €ģž„" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "ģ“ė¦„" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "ģ»¤ė§Øė“œ ė¼ģø" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "실행할 ķŒŒģ¼ ģ„ ķƒ" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "ģ—“źø°" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "ė°ģŠ¤ķ¬ķ†±" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "ģ¹“ė©”ė¼" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "ķ“ė¦½ė³“ė“œ ė™źø°ķ™”" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "미디얓 ķ”Œė ˆģ“ģ–“" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "ė§ˆģš°ģŠ¤ģ™€ ķ‚¤ė³“ė“œ" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "ģŒėŸ‰ ģ œģ–“" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "ķŒŒģ¼" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "ķŒŒģ¼ 받기" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "ķŒŒģ¼ė”œ ģ €ģž„" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "공유 중" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "ģž„ģ¹˜ 배터리" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "배터리 부씱 ģ•Œė¦¼" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "ģ¶©ģ „ ģ™„ė£Œ ģ•Œė¦¼" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "ģ‹œģŠ¤ķ…œ 배터리" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "통계 공유" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "배터리" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "ėŖ…ė ¹" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "ėŖ…ė ¹ 추가" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "ģ•Œė¦¼ 공유" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "ķ™œģ„±ķ™”ė  ė•Œ 공유" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "ķ”„ė”œź·øėžØ" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "ģ•Œė¦¼" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "ģ—°ė½ģ²˜" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "ģˆ˜ģ‹  ģ „ķ™”" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "볼넨" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "미디얓 ģ¼ģ‹œģ •ģ§€" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "ė°œģ‹  ģ „ķ™”" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "ė§ˆģ“ķ¬ ģŒģ†Œź±°" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "ģ „ķ™”" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "ė™ģž‘ ė°”ė”œź°€źø°" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "ėŖØė“  설정 ģ“ˆźø°ķ™”" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "ė°”ė”œź°€źø°" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "ķ”ŒėŸ¬ź·øģø" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "ģ‹¤ķ—˜ģ  기늄" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "ģž„ģ¹˜ ģŗģ‹œ" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "ģŗģ‹œ ģ‚­ģ œ" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "ė ˆź±°ģ‹œ SMS 지원" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "SFTP ģžė™ 마욓트" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "ź³ źø‰" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "단축 키" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "ģž„ģ¹˜ 설정" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "ķŽ˜ģ–“ė§" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "ė””ė°”ģ“ģŠ¤ģ˜ ķŽ˜ģ–“ė§ģ“ ķ•“ģ œė˜ģ—ˆģŠµė‹ˆė‹¤" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "ķŽ˜ģ–“ė§ 전에 ė””ė°”ģ“ģŠ¤ė„¼ 설정핓야 ķ•©ė‹ˆė‹¤" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "ģ•”ķ˜øķ™” 정볓" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "ķŽ˜ģ–“ė§ ķ•“ģ œ" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "ė‚“ 기기딜 전솔" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "ė‚“ źø°źø°ģ—ģ„œ ė°›ģ•„ģ˜¤źø°" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "ģ—†ģŒ" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "복원" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "ė‚®ź²Œ" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "ė¬“ģŒ" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "설정" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Esc넼 눌러 ģ·Øģ†Œķ•˜ź±°ė‚˜ ė°±ģŠ¤ķŽ˜ģ“ģŠ¤ 키넼 눌러 단축 키넼 ģ“ˆźø°ķ™”ķ•©ė‹ˆė‹¤." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "ė””ė°”ģ“ģŠ¤ ģ“ė¦„" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "ģ“ė¦„ 변경" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "새딜고침" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "ėŖØė°”ģ¼ 설정" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "ģ„œė¹„ģŠ¤ 메뉓" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "ģž„ģ¹˜ 메뉓" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "ģž„ģ¹˜ ģ“ė¦„ 바꾸기" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "ģž„ģ¹˜" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "ģž„ģ¹˜ė„¼ ģ°¾ėŠ” 중…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "ėøŒė¼ģš°ģ € ķ™•ģž„ 기늄" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "ģ‚¬ģš©" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "ģ“ źø°źø°ėŠ” ķŽ˜ģ–“ė§ė˜ģ§€ ģ•Šģ•˜ģœ¼ėÆ€ė”œ ė³“ģ“ģ§€ ģ•ŠģŠµė‹ˆė‹¤" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Discovery ė¹„ķ™œģ„±ķ™”" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "볓기 ėŖØė“œ" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "ķŒØė„" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "ģ‚¬ģš©ģž 메뉓" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "지원 딜그 ģƒģ„±" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "GSConnect 정볓" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "ģž„ģ¹˜ ģ„ ķƒ" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "ģ„ ķƒ" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "ģž„ģ¹˜ė„¼ ģ°¾ģ„ 수 ģ—†ģŒ" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "ģž„ģ¹˜ ėŖ©ė”" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "źø°ė”" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "묓언가가 ģž˜ėŖ»ėœ ź±° ź°™ģŠµė‹ˆė‹¤" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "GSConnect에 예상치 ėŖ»ķ•œ ģ˜¤ė„˜ź°€ ė°œģƒķ–ˆģŠµė‹ˆė‹¤." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "ģžģ„øķžˆ 볓기" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "ėŖØė°”ģ¼ 기기딜 전솔" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "ėŖØė°”ģ¼ źø°źø°" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "켜기" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "ķŽøģ§‘" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "제거" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "ģ‚¬ģš© ģ•ˆ 함" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "%s ė°”ė”œź°€źø°ė„¼ ėŒ€ģ²“ķ•  새딜욓 ė°”ė”œź°€źø°ė„¼ ģž…ė „ķ•˜ģ‹­ģ‹œģ˜¤" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s(ģ€)ėŠ” ģ“ėÆø ģ‚¬ģš© ģ¤‘ģž…ė‹ˆė‹¤" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "ź·øė†ˆģ„ ģœ„ķ•œ KDE Connectģ˜ ķ˜øķ™˜ 기늄" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "UtsushimiNeneka(네네칓)" #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "디버그 ė©”ģ‹œģ§€ź°€ ė”œź·øģ— źø°ė”ė©ė‹ˆė‹¤." #: src/preferences/service.js:414 msgid "Review Log" msgstr "딜그 ķ™•ģø" #: src/preferences/service.js:482 msgid "Laptop" msgstr "ė…øķŠøė¶" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "ģŠ¤ė§ˆķŠøķ°" #: src/preferences/service.js:486 msgid "Tablet" msgstr "ķƒœėø”ė¦æ" #: src/preferences/service.js:488 msgid "Television" msgstr "TV" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "ķŽ˜ģ–“ė§ ķ•“ģ œėØ" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "ģ—°ź²°ė˜ģ§€ ģ•ŠģŒ" #: src/preferences/service.js:518 msgid "Connected" msgstr "연결됨" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "ģ„œė¹„ģŠ¤ė„¼ źø°ė‹¤ė¦¬ėŠ” 중" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "ģ „ķ™”ė²ˆķ˜ø" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "ķŒŒģ¼ 공유" #: src/service/daemon.js:351 msgid "List available devices" msgstr "ģ‚¬ģš© ź°€ėŠ„ķ•œ ģž„ģ¹˜ ėŖ©ė”" #: src/service/daemon.js:360 msgid "List all devices" msgstr "ėŖØė“  ģž„ģ¹˜ 볓기" #: src/service/daemon.js:369 msgid "Target Device" msgstr "ėŒ€ģƒ źø°źø°" #: src/service/daemon.js:411 msgid "Message Body" msgstr "ė©”ģ‹œģ§€ ė‚“ģš©" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "ģ•Œė¦¼ 볓낓기" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "ģ•Œė¦¼ 앱 ģ“ė¦„" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "ģ•Œė¦¼ ė‚“ģš©" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "ģ•Œė¦¼ ģ•„ģ“ģ½˜" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "ģ•Œė¦¼ ID" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "사진" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "ķ•‘" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "벨" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "링크 공유" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "ķ…ģŠ¤ķŠø 공유" #: src/service/daemon.js:528 msgid "Show release version" msgstr "릓리즈 버전 볓기" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "ģ‚¬ģš©ķ•  수 ģ—†ģŒ" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "%s(으)ė”œė¶€ķ„° ģ—°ź²° ģš”ģ²­ėØ" #: src/service/device.js:800 msgid "Reject" msgstr "ź±°ė¶€" #: src/service/device.js:805 msgid "Accept" msgstr "ė™ģ˜" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "" #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSLģ„ ģ°¾ģ„ 수 ģ—†ģŒ" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "ķ¬ķŠøź°€ ģ‚¬ģš© ģ¤‘ģž…ė‹ˆė‹¤" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: ģ¶©ģ „ģ“ ģ™„ė£Œė˜ģ—ˆģŠµė‹ˆė‹¤" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "ģ¶©ģ „ ģ™„ė£Œ" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: 배터리가 ė¶€ģ”±ķ•©ė‹ˆė‹¤" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% ė‚ØģŒ" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "ķ“ė¦½ė³“ė“œ" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "ķ“ė¦½ė³“ė“œ 볓낓기" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "ķ“ė¦½ė³“ė“œ 받기" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "ė‚“ ķœ“ėŒ€ģ „ķ™” 찾기" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "ķŠøėž™ķŒØė“œ" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "ķ‚¤ė³“ė“œ" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "ģ•Œ 수 ģ—†ģŒ" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "ģ•Œė¦¼ ģ·Øģ†Œ" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "ģ•Œė¦¼ ė‹«źø°" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "ģ•Œė¦¼ ė‹µģž„" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "ģ•Œė¦¼ ķ™œģ„±ķ™”" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "전솔 ģ‹¤ķŒØ" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "\"%s\"넼 %s에 ė³“ė‚“ėŠ” ė° ģ‹¤ķŒØķ–ˆģŠµė‹ˆė‹¤." #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "ģ—°ė½: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "ķ”„ė ˆģ  ķ…Œģ“ģ…˜" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "ėŖ…ė ¹ 실행" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "마욓트" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "마욓트 ķ•“ģ œ" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "공유" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s딜 ķŒŒģ¼ģ„ 올릓 수 ģ—†ģŠµė‹ˆė‹¤." #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "ķŒŒģ¼ 전솔 중" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "\"%s\"넼 %sģ—ģ„œ ė°›ėŠ” 중" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "전솔 성공" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "\"%s\"넼 %sģ—ģ„œ ė°›ģ•˜ģŠµė‹ˆė‹¤." #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "ķ“ė” ģ—“źø°" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "ķŒŒģ¼ ģ—“źø°" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "\"%s\"넼 %sģ—ģ„œ ė°›ģ•„ģ˜¤ėŠ” ė° ģ‹¤ķŒØķ–ˆģŠµė‹ˆė‹¤." #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "%s(으)ė”œė¶€ķ„° ķ…ģŠ¤ķŠøź°€ 공유됨" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "\"%s\" ķŒŒģ¼ģ„ %s(으)딜 ė³“ė‚“ėŠ” 중" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "\"%s\" ķŒŒģ¼ģ„ %s(으)딜 ė³“ėƒˆģŠµė‹ˆė‹¤." #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "%s딜 ķŒŒģ¼ 볓낓기" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "ģ™„ė£Œė˜ė©“ ģ—“źø°" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "%s딜 링크 볓낓기" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "ė©”ģ‹œģ§€" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "새 ė©”ģ‹œģ§€" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "ė©”ģ‹œģ§€ ė‹µģž„" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "ė©”ģ‹œģ§€ 공유" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "ģ‹œģŠ¤ķ…œ 볼넨" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio넼 ģ°¾ģ„ 수 ģ—†ģŒ" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "ė¬“ģŒ 통화" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "ģ•Œ 수 ģ—†ėŠ” ģ—°ė½ģ²˜" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "ģˆ˜ģ‹  ģ „ķ™”" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "ė°œģ‹  ģ „ķ™”" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "팩스" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "ģ§ģž„" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "ķœ“ėŒ€ģ „ķ™”" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "ķ™ˆ" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "%s딜 볓낓기" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "방금" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "ģ–“ģ œ %s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%dė¶„" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "그룹 ė©”ģ‹œģ§€" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "%s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "외 %dėŖ…" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "%sģ˜ 원격 ķ‚¤ė³“ė“œź°€ ķ™œģ„±ķ™”ė˜ģ§€ ģ•ŠģŒ" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (츔정 중…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (충전될 ė•Œź¹Œģ§€ %d : %02d)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d : %02d ė‚ØģŒ)" #: src/shell/notification.js:54 msgid "Reply" msgstr "ė‹µģž„" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "GSConnect딜 링크넼 ź³µģœ ķ•˜ģ—¬ 웹 ėøŒė¼ģš°ģ €ģ—ģ„œ ģ—“ź±°ė‚˜ ė©”ģ‹œģ§€ė„¼ 통핓 ź³µģœ ķ•©ė‹ˆė‹¤." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "ģ„œė¹„ģŠ¤ ģ•ˆ 됨" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "웹 ėøŒė¼ģš°ģ €ģ—ģ„œ ģ—“źø°" gnome-shell-extension-gsconnect-50/po/ko.po000066400000000000000000000734651421543444100211150ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the org.gnome.Shell.Extensions.GSConnect package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: org.gnome.Shell.Extensions.GSConnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-12-18 19:41+0100\n" "PO-Revision-Date: 2021-08-31 02:02+0900\n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Poedit 3.0\n" "Last-Translator: \n" "Language: ko\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect ėŖØė°”ģ¼ ģ—°ė™" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "GSConnect ķŒ€" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "" "GSConnect is a complete implementation of KDE Connect especially for GNOME " "Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team " "has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "" "With GSConnect you can securely connect to mobile devices and other desktops " "to:" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "ķŒŒģ¼ź³¼ 링크, ķ…ģŠ¤ķŠø 공유" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "ė©”ģ‹œģ§€ė„¼ 주고받기" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "ķ“ė¦½ė³“ė“œ ė‚“ģš© ė™źø°ķ™”" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "ģ—°ė½ģ²˜ ė™źø°ķ™”" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "ģ•Œė¦¼ ė™źø°ķ™”" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "미디얓 ģž¬ģƒźø° ģ œģ–“" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "ģ‹œģŠ¤ķ…œ ģŒėŸ‰ ģ œģ–“" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "사전 ģ„¤ģ •ėœ 명령얓 실행" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "ź·ø 외…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:93 msgid "GSConnect in GNOME Shell" msgstr "ź·øė†ˆ ģ‰˜ģ˜ GSConnect ėŖØė°”ģ¼ ģ—°ė™" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "ģ—°ź²°ķ•˜źø°ā€¦" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:158 #: src/service/plugins/share.js:284 src/service/plugins/share.js:415 msgid "Cancel" msgstr "ģ·Øģ†Œ" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "ģ—°ź²°" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IPģ£¼ģ†Œ" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "ģ—°ė½ģ²˜ ģ—†ģŒ" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "ė„ģ›€ė§" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "ķœ“ėŒ€ģ „ķ™” 번호 ė° ģ“ė¦„ ģž…ė „" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "źø°ķƒ€" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:59 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "ė©”ģ‹œģ§€ė”œ 볓낓기" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:416 msgid "Send" msgstr "볓낓기" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "ģ—°ź²°ģ“ ķ•“ģ œė˜ģ—ˆģŠµė‹ˆė‹¤" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:51 msgid "Send Message" msgstr "ė©”ģ‹œģ§€ 볓낓기" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "ė©”ģ‹œģ§€ ģž…ė „" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "ė©”ģ‹œģ§€ ėŖ©ė”" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "ė©”ģ‹œģ§€ė„¼ ģž…ė „ķ•˜ź³  Enter넼 ėˆ„ė„“ė©“ ė³“ėƒ…ė‹ˆė‹¤" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:27 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "ėŒ€ķ™”" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "새 ėŒ€ķ™”" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "ėŒ€ķ™” ģ—†ģŒ" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "ģ„ ķƒėœ ėŒ€ķ™” ģ—†ģŒ" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "ėŒ€ķ™”ė„¼ ģ‹œģž‘ķ•˜ź±°ė‚˜ ģ„ ķƒķ•˜ģ‹­ģ‹œģ˜¤" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "ėŖ…ė ¹ ķŽøģ§‘" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "ģ €ģž„" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "ģ“ė¦„" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "명령얓" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "ģ—“źø°" #: data/ui/preferences-device-panel.ui:39 src/preferences/service.js:490 msgid "Desktop" msgstr "ė°ģŠ¤ķ¬ķƒ‘" #: data/ui/preferences-device-panel.ui:88 msgid "Camera" msgstr "ģ¹“ė©”ė¼" #: data/ui/preferences-device-panel.ui:145 msgid "Clipboard Sync" msgstr "ķ“ė¦½ė³“ė“œ ė™źø°ķ™”" #: data/ui/preferences-device-panel.ui:211 msgid "Media Players" msgstr "미디얓 ģž¬ģƒźø°" #: data/ui/preferences-device-panel.ui:268 msgid "Mouse & Keyboard" msgstr "마우스 & ķ‚¤ė³“ė“œ" #: data/ui/preferences-device-panel.ui:325 msgid "Volume Control" msgstr "볼넨 ģ œģ–“" #: data/ui/preferences-device-panel.ui:379 src/service/plugins/sftp.js:378 msgid "Files" msgstr "ķŒŒģ¼" #: data/ui/preferences-device-panel.ui:431 msgid "Receive Files" msgstr "ķŒŒģ¼ 받기" #: data/ui/preferences-device-panel.ui:490 msgid "Save files to" msgstr "ķŒŒģ¼ ģ €ģž„" #: data/ui/preferences-device-panel.ui:551 #: data/ui/preferences-device-panel.ui:2132 msgid "Sharing" msgstr "공유" #: data/ui/preferences-device-panel.ui:582 msgid "Device Battery" msgstr "ģž„ģ¹˜ 배터리" #: data/ui/preferences-device-panel.ui:633 msgid "Low Battery Notification" msgstr "배터리 부씱 ģ•Œė¦¼" #: data/ui/preferences-device-panel.ui:692 msgid "Fully Charged Notification" msgstr "ģ¶©ģ „ ģ™„ė£Œ ģ•Œė¦¼" #: data/ui/preferences-device-panel.ui:746 msgid "System Battery" msgstr "ģ‹œģŠ¤ķ…œ 배터리" #: data/ui/preferences-device-panel.ui:795 msgid "Share Statistics" msgstr "상태 공유" #: data/ui/preferences-device-panel.ui:849 #: data/ui/preferences-device-panel.ui:2178 src/service/plugins/battery.js:12 msgid "Battery" msgstr "배터리" #: data/ui/preferences-device-panel.ui:879 #: data/ui/preferences-device-panel.ui:964 #: data/ui/preferences-device-panel.ui:2224 #: src/service/plugins/runcommand.js:23 src/service/plugins/runcommand.js:31 #: src/service/plugins/runcommand.js:182 msgid "Commands" msgstr "ėŖ…ė ¹" #: data/ui/preferences-device-panel.ui:938 msgid "Add Command" msgstr "ėŖ…ė ¹ 추가" #: data/ui/preferences-device-panel.ui:1025 msgid "Share Notifications" msgstr "ģ•Œė¦¼ 공유" #: data/ui/preferences-device-panel.ui:1085 msgid "Share When Active" msgstr "ķ™œģ„±ķ™” ģ‹œ 공유" #: data/ui/preferences-device-panel.ui:1136 msgid "Applications" msgstr "ķ”„ė”œź·øėžØ" #: data/ui/preferences-device-panel.ui:1182 #: data/ui/preferences-device-panel.ui:2270 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "ģ•Œė¦¼" #: data/ui/preferences-device-panel.ui:1240 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "ģ—°ė½ģ²˜" #: data/ui/preferences-device-panel.ui:1293 msgid "Incoming Calls" msgstr "ģˆ˜ģ‹  ģ „ķ™”" #: data/ui/preferences-device-panel.ui:1342 #: data/ui/preferences-device-panel.ui:1509 msgid "Volume" msgstr "ģŒėŸ‰" #: data/ui/preferences-device-panel.ui:1408 #: data/ui/preferences-device-panel.ui:1575 msgid "Pause Media" msgstr "미디얓 ģ¼ģ‹œģ •ģ§€" #: data/ui/preferences-device-panel.ui:1461 msgid "Ongoing Calls" msgstr "ģ „ķ™” ź±ø ė•Œ" #: data/ui/preferences-device-panel.ui:1631 msgid "Mute Microphone" msgstr "ė§ˆģ“ķ¬ 끄기" #: data/ui/preferences-device-panel.ui:1685 #: data/ui/preferences-device-panel.ui:2316 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "ģ „ķ™”" #: data/ui/preferences-device-panel.ui:1720 msgid "Action Shortcuts" msgstr "ė™ģž‘ ė°”ė”œź°€źø°" #: data/ui/preferences-device-panel.ui:1736 msgid "Reset All…" msgstr "모두 ģ“ˆźø°ķ™”ā€¦" #: data/ui/preferences-device-panel.ui:1788 msgid "Shortcuts" msgstr "ė°”ė”œź°€źø°" #: data/ui/preferences-device-panel.ui:1819 msgid "Plugins" msgstr "ķ”ŒėŸ¬ź·øģø" #: data/ui/preferences-device-panel.ui:1866 msgid "Experimental" msgstr "" #: data/ui/preferences-device-panel.ui:1913 msgid "Device Cache" msgstr "ģž„ģ¹˜ ģŗģ‹œ" #: data/ui/preferences-device-panel.ui:1931 msgid "Clear Cache…" msgstr "ģŗģ‹œ ģ‚­ģ œā€¦" #: data/ui/preferences-device-panel.ui:1970 msgid "Legacy SMS Support" msgstr "ė ˆź±°ģ‹œ SMS 지원" #: data/ui/preferences-device-panel.ui:2027 msgid "SFTP Automount" msgstr "SFTP ģžė™ 마욓트" #: data/ui/preferences-device-panel.ui:2082 #: data/ui/preferences-device-panel.ui:2408 msgid "Advanced" msgstr "ź³ źø‰" #: data/ui/preferences-device-panel.ui:2362 msgid "Keyboard Shortcuts" msgstr "ķ‚¤ė³“ė“œ 단축키" #: data/ui/preferences-device-panel.ui:2426 msgid "Device Settings" msgstr "ģž„ģ¹˜ 설정" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2470 #: data/ui/preferences-device-panel.ui:2562 src/service/daemon.js:381 msgid "Pair" msgstr "ģ—°ė™" #: data/ui/preferences-device-panel.ui:2502 msgid "Device is unpaired" msgstr "ķŽ˜ģ–“ė§ģ“ ķ•“ģ œė˜ģ—ˆģŠµė‹ˆė‹¤." #: data/ui/preferences-device-panel.ui:2517 msgid "You may configure this device before pairing" msgstr "먼저 ėŖØė°”ģ¼ ģž„ģ¹˜ė„¼ ģ„¤ģ •ķ•˜ģ‹­ģ‹œģ˜¤." #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2557 msgid "Encryption Info" msgstr "ģ•”ķ˜øķ™” 정볓" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2568 src/service/daemon.js:390 msgid "Unpair" msgstr "ķŽ˜ģ–“ė§ ķ•“ģ œ" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2580 msgid "To Device" msgstr "ģž„ģ¹˜ģ— 볓낓기" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2586 msgid "From Device" msgstr "ģž„ģ¹˜ģ—ģ„œ ė°›ģ•„ģ˜¤źø°" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2598 #: data/ui/preferences-device-panel.ui:2631 msgid "Nothing" msgstr "ź·øėŒ€ė”œ" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2605 #: data/ui/preferences-device-panel.ui:2638 msgid "Restore" msgstr "ė˜ėŒė¦¬źø°" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2612 #: data/ui/preferences-device-panel.ui:2645 msgid "Lower" msgstr "낮추기" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2619 #: data/ui/preferences-device-panel.ui:2652 #: src/service/plugins/telephony.js:194 msgid "Mute" msgstr "ė¬“ģŒ" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "설정" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Esc 키넼 ėˆŒėŸ¬ģ„œ ģ·Øģ†Œķ•˜ź³  ė°±ģŠ¤ķŽ˜ģ“ģŠ¤ 키넼 눌러 ģ“ˆźø°ķ™”ķ•©ė‹ˆė‹¤." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "ģž„ģ¹˜ ģ“ė¦„" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "ģ“ė¦„ 바꾸기(_R)" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "새딜 고침" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "ėŖØė°”ģ¼ 설정" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "ģ„œė¹„ģŠ¤ 메뉓" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "ģž„ģ¹˜ 메뉓" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "ģž„ģ¹˜ ģ“ė¦„ 바꾸기" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "ģž„ģ¹˜" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "ģž„ģ¹˜ ź²€ģƒ‰ā€¦" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "웹 ėøŒė¼ģš°ģ € ķ™•ģž„ 기늄" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "ģ‚¬ģš©" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "ķ‘œģ‹œ ė°©ģ‹" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "ķŒØė„" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "ģ‚¬ģš©ģž 메뉓" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "지원 źø°ė” ģƒģ„±" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "GSConnect 정볓" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "ģž„ģ¹˜ ģ„ ķƒ" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "ģ„ ķƒ" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "ģž„ģ¹˜ė„¼ ģ°¾ģ„ 수 ģ—†ģŒ" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "ģž„ģ¹˜ ėŖ©ė”" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "볓고" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "ģ˜¤ė„˜ź°€ ė°œģƒķ–ˆģŠµė‹ˆė‹¤" #: data/ui/service-error-dialog.ui:84 msgid "" "GSConnect encountered an unexpected error. Please report the problem and " "include any information that may help." msgstr "GSConnect ėŖØė°”ģ¼ ģ—°ė™ģ— 예기치 ėŖ»ķ•œ ģ˜¤ė„˜ź°€ ė°œģƒķ–ˆģŠµė‹ˆė‹¤." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "기술적 정볓" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "ėŖØė°”ģ¼ 기기에 전솔" #. Service Menu #: src/extension.js:91 msgid "Mobile Devices" msgstr "ėŖØė°”ģ¼ ģ—°ė™" #: src/extension.js:119 msgid "Turn On" msgstr "켜기" #: src/extension.js:377 msgid "Turn Off" msgstr "끄기" #: src/preferences/device.js:655 src/preferences/device.js:661 msgid "Edit" msgstr "ķŽøģ§‘" #: src/preferences/device.js:670 src/preferences/device.js:676 msgid "Remove" msgstr "ģ‚­ģ œ" #: src/preferences/device.js:806 src/preferences/device.js:819 msgid "On" msgstr "켬" #: src/preferences/device.js:806 src/preferences/device.js:819 msgid "Off" msgstr "끔" #: src/preferences/device.js:921 src/preferences/device.js:949 msgid "Disabled" msgstr "ģ‚¬ģš© ģ•ˆ 함" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "새 ė°”ė”œź°€źø° 키넼 ģž…ė „%s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%sėŠ” ģ“ėÆø ģ‚¬ģš© ģ¤‘ģž…ė‹ˆė‹¤" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "" "kuroehanako\n" "" #: src/preferences/service.js:411 msgid "" "Debug messages are being logged. Take any steps necessary to reproduce a " "problem then review the log." msgstr "" "디버그 ė©”ģ‹œģ§€ź°€ źø°ė” ģ¤‘ģž…ė‹ˆė‹¤. Take any steps necessary to reproduce a " "problem then review the log." #: src/preferences/service.js:414 msgid "Review Log" msgstr "źø°ė” 볓기" #: src/preferences/service.js:482 msgid "Laptop" msgstr "ė…øķŠøė¶" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "ģŠ¤ė§ˆķŠøķ°" #: src/preferences/service.js:486 msgid "Tablet" msgstr "ķƒœėø”ė¦æ" #: src/preferences/service.js:488 msgid "Television" msgstr "TV" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "ķŽ˜ģ–“ė§ ģ•ˆ 됨" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "ģ—°ź²°ė˜ģ§€ ģ•ŠģŒ" #: src/preferences/service.js:518 msgid "Connected" msgstr "ģ—°ź²° 됨" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "ģ„œė¹„ģŠ¤ė„¼ źø°ė‹¤ė¦¬ėŠ” 중…" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "ė‹¤ģ“ģ–¼ 번호" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:28 msgid "Share File" msgstr "ķŒŒģ¼ 공유" #: src/service/daemon.js:351 msgid "List available devices" msgstr "ģ‚¬ģš© ź°€ėŠ„ķ•œ ģž„ģ¹˜ ėŖ©ė”" #: src/service/daemon.js:360 msgid "List all devices" msgstr "ėŖØė“  ģž„ģ¹˜ ķ‘œģ‹œ" #: src/service/daemon.js:369 msgid "Target Device" msgstr "ėŒ€ģƒ ģž„ģ¹˜" #: src/service/daemon.js:411 msgid "Message Body" msgstr "ė©”ģ‹œģ§€ ė‚“ģš©" #: src/service/daemon.js:423 src/service/plugins/notification.js:53 msgid "Send Notification" msgstr "ģ•Œė¦¼ 전솔" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "ģ•Œė¦¼ 앱 ģ“ė¦„" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "ģ•Œė¦¼ ė‚“ģš©" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "ģ•Œė¦¼ ģ•„ģ“ģ½˜" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "ģ•Œė¦¼ ID" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:24 msgid "Photo" msgstr "사진" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:17 src/service/plugins/ping.js:44 msgid "Ping" msgstr "ķ•‘" #: src/service/daemon.js:486 src/service/plugins/battery.js:243 #: src/service/plugins/battery.js:272 src/service/plugins/findmyphone.js:19 msgid "Ring" msgstr "ė²Øģ†Œė¦¬ 울리기" #: src/service/daemon.js:507 src/service/plugins/share.js:44 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "링크 공유" #: src/service/daemon.js:516 src/service/plugins/share.js:36 msgid "Share Text" msgstr "ķ…ģŠ¤ķŠø 공유" #: src/service/daemon.js:528 msgid "Show release version" msgstr "" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:789 #, javascript-format msgid "Pair Request from %s" msgstr "%s(으)ė”œė¶€ķ„° ģ—°ź²° ģš”ģ²­ģ“ ė“¤ģ–“ģ™”ģŠµė‹ˆė‹¤" #: src/service/device.js:796 msgid "Reject" msgstr "ź±°ė¶€" #: src/service/device.js:801 msgid "Accept" msgstr "ģˆ˜ė½" #: src/service/manager.js:114 msgid "" "Discovery has been disabled due to the number of devices on this network." msgstr "" #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSLģ„ ģ°¾ģ„ 수 ģ—†ģŠµė‹ˆė‹¤" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "ģ“ėÆø ģ‚¬ģš© ģ¤‘ģø ķ¬ķŠø" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:252 #, javascript-format msgid "%s: Battery is full" msgstr "%s: 충전됨" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:254 src/shell/device.js:118 msgid "Fully Charged" msgstr "ģ¶©ģ „ģ“ ģ™„ė£Œė˜ģ—ˆģŠµė‹ˆė‹¤." #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:281 #, javascript-format msgid "%s: Battery is low" msgstr "%s: 배터리 부씱" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:283 #, javascript-format msgid "%d%% remaining" msgstr "%d%% ė‚ØģŒ" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "ķ“ė¦½ė³“ė“œ" #: src/service/plugins/clipboard.js:22 msgid "Clipboard Push" msgstr "ķ“ė¦½ė³“ė“œ 볓낓기" #: src/service/plugins/clipboard.js:30 msgid "Clipboard Pull" msgstr "ķ“ė¦½ė³“ė“œ ė°›ģ•„ģ˜¤źø°" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/contacts.js:235 src/service/plugins/contacts.js:355 #: src/service/plugins/telephony.js:152 src/service/plugins/telephony.js:171 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "ģ•Œ 수 ģ—†ėŠ” ģ—°ė½ģ²˜" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "ė‚“ ķœ“ėŒ€ģ „ķ™” 찾기" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "ķ„°ģ¹˜ķŒØė“œ" #: src/service/plugins/mousepad.js:26 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "ķ‚¤ė³“ė“œ" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:298 msgid "Unknown" msgstr "ģ•Œ 수 ģ—†ģŒ" #: src/service/plugins/notification.js:29 msgid "Cancel Notification" msgstr "ģ•Œė¦¼ ģ·Øģ†Œ" #: src/service/plugins/notification.js:37 msgid "Close Notification" msgstr "" #: src/service/plugins/notification.js:45 msgid "Reply Notification" msgstr "" #: src/service/plugins/notification.js:61 msgid "Activate Notification" msgstr "ģ•Œė¦¼ ķ™œģ„±ķ™”" #: src/service/plugins/photo.js:219 src/service/plugins/share.js:127 #: src/service/plugins/share.js:197 src/service/plugins/share.js:307 msgid "Transfer Failed" msgstr "전솔 ģ‹¤ķŒØ" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:221 src/service/plugins/share.js:309 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "\"%s\"(ģ„)넼 %s(으)딜 전솔할 수 ģ—†ģŒ" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:51 #, javascript-format msgid "Ping: %s" msgstr "" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "ė°œķ‘œ" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "ėŖ…ė ¹ 실행" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:19 msgid "Mount" msgstr "마욓트" #: src/service/plugins/sftp.js:27 msgid "Unmount" msgstr "마욓트 ķ•“ģ œ" #: src/service/plugins/share.js:14 src/service/plugins/share.js:20 msgid "Share" msgstr "공유" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:129 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s(ģ“)ź°€ ķŒŒģ¼ ģ—…ė”œė“œė„¼ ķ—ˆģš©ķ•˜ģ§€ ģ•ŠģŒ" #: src/service/plugins/share.js:151 src/service/plugins/share.js:277 msgid "Transferring File" msgstr "ķŒŒģ¼ 전솔 중" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:153 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "%s(으)딜 \"%s\"(ģ„)넼 전솔 중" #: src/service/plugins/share.js:172 src/service/plugins/share.js:297 msgid "Transfer Successful" msgstr "전솔 성공" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:174 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "%sģ—ģ„œ \"%s\"(ģ„)넼 ė°›ģŒ" #: src/service/plugins/share.js:180 msgid "Open Folder" msgstr "ķ“ė” ģ—“źø°" #: src/service/plugins/share.js:185 msgid "Open File" msgstr "ķŒŒģ¼ ģ—“źø°" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:199 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "%s(으)딜 \"%s\"(ģ„)넼 ģ „ģ†”ķ•˜ėŠ” ė° ģ‹¤ķŒØķ–ˆģŠµė‹ˆė‹¤" #: src/service/plugins/share.js:229 #, javascript-format msgid "Text Shared By %s" msgstr "%sģ—ģ„œ 공유된 ķ…ģŠ¤ķŠø" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:279 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "\"%s\"(ģ„)넼 %s(으)딜 ė³“ė‚“ėŠ” 중" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:299 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "\"%s\"(ģ„)넼 %s(으)딜 ė³“ėƒˆģŠµė‹ˆė‹¤" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:367 #, javascript-format msgid "Send files to %s" msgstr "%s(으)딜 ķŒŒģ¼ 전솔 중" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:371 msgid "Open when done" msgstr "ģ™„ė£Œ ģ‹œ ģ—“źø°" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:410 #, javascript-format msgid "Send a link to %s" msgstr "%s(으)딜 링크 볓낓기" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS ė©”ģ‹œģ§€" #: src/service/plugins/sms.js:35 msgid "New SMS (URI)" msgstr "새 ė©”ģ‹œģ§€" #: src/service/plugins/sms.js:43 msgid "Reply SMS" msgstr "SMS ė‹µģž„" #: src/service/plugins/sms.js:67 msgid "Share SMS" msgstr "SMS 공유" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "ģ‹œģŠ¤ķ…œ ģŒėŸ‰" #: src/service/plugins/systemvolume.js:55 msgid "PulseAudio not found" msgstr "PulseAudio넼 ģ°¾ģ„ 수 ģ—†ģŠµė‹ˆė‹¤" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:25 msgid "Mute Call" msgstr "ė¬“ģŒ" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:190 msgid "Incoming call" msgstr "ģ „ķ™” 올 ė•Œ" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:205 msgid "Ongoing call" msgstr "ģ „ķ™” ź±ø ė•Œ" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "팩스" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "ģ§ģž„" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "ķœ“ėŒ€ģ „ķ™”" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "ģ§‘" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "%s딜 ģ „ķ™” ź±øźø°" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "방금" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "ģ–“ģ œćƒ»%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%dė¶„" #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "ģ‚¬ģš©ķ•  수 ģ—†ģŒ" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "그룹 ė©”ģ‹œģ§€" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "ģƒėŒ€: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "%d 외" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (예상 중…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d : ģ¶©ģ „ 중 %02d)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d: %02d ė‚ØģŒ)" #: src/shell/notification.js:54 msgid "Reply" msgstr "ė‹µģž„" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "" #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "ģ„œė¹„ģŠ¤ė„¼ ģ‚¬ģš©ķ•  수 ģ—†ģŒ" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "ėøŒė¼ģš°ģ €ģ—ģ„œ ģ—“źø°" gnome-shell-extension-gsconnect-50/po/lt.po000066400000000000000000001065001421543444100211060ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-12-11 18:05\n" "Last-Translator: \n" "Language-Team: Lithuanian\n" "Language: lt_LT\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n%10==1 && (n%100>19 || n%100<11) ? 0 : (n%10>=2 && n%10<=9) && (n%100>19 || n%100<11) ? 1 : n%1!=0 ? 2: 3);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: lt\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "KDE Connect ÄÆgyvendinimas, skirtas GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "GSConnect komanda" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnect yra pilnas KDE Connect ÄÆgyvendinimas, ypatingai skirtas integracijai su GNOME Shell ir Nautilus, Chrome bei Firefox. KDE Connect komanda turi programas, skirtas Linux, BSD, Android, Sailfish, macOS ir Windows sistemoms." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "Naudodami GSConnect galite saugiai prisijungti prie mobiliųjų ÄÆrenginių ir kitų stalinių kompiuterių norėdami:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Bendrinti failus, nuorodas ir tekstą" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Siųsti ir gauti žinutes" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Sinchronizuoti iÅ”karpinės turinÄÆ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Sinchronizuoti adresatus" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Sinchronizuoti praneÅ”imus" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Valdyti medijos leistuves" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "Valdyti sistemos garsÄÆ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Vykdyti iÅ” anksto nustatytas komandas" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "Ir daugiau…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect kartu su GNOME Shell" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Prisijungti prie…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Atsisakyti" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Prisijungti" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP adresas" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "Adresatų nėra" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Žinynas" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Ä®raÅ”ykite telefono numerÄÆ ar vardą" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Kitas" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "Siųsti SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Siųsti" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Ä®renginys yra atsijungęs" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Siųsti žinutę" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "RaÅ”yti žinutę" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "Žinutės ÄÆvedimas" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "RaÅ”ykite žinutę ir norėdami iÅ”siųsti paspauskite ā€žEnterā€œ" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "SusiraÅ”inėjimai" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Naujas pokalbis" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Pokalbių nėra" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Nepasirinktas joks pokalbis" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Pasirinkite arba pradėkite pokalbÄÆ" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Taisyti komandą" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Ä®raÅ”yti" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Pavadinimas" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Komandų eilutė" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Pasirinkti vykdomąjÄÆ failą" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Atverti" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Stalinis kompiuteris" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "Kamera" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "IÅ”karpinės sinchronizavimas" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Medijos leistuvės" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Pelė ir klaviatÅ«ra" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Garsumo reguliavimas" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Failai" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Gauti failus" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Ä®raÅ”yti failus ÄÆ" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Bendrinimas" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Ä®renginio baterija" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "PraneÅ”imas apie žemą baterijos ÄÆkrovos lygÄÆ" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "PraneÅ”imas apie ÄÆkrovimą iki tinkinto lygio" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "PraneÅ”imas apie pilnai ÄÆkrauta bateriją" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Sistemos baterija" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Bendrinti statistiką" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Baterija" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Komandos" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Pridėti komandą" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Bendrinimo praneÅ”imai" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Bendrinti, kai aktyvus" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Programos" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "PraneÅ”imai" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Adresatai" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Gaunami skambučiai" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Garsumas" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Pristabdyti mediją" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Vykstantys skambučiai" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Nutildyti mikrofoną" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Telefonija" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Veiksmų trumpiniai" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Atstatyti visus…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Trumpiniai" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Ä®skiepiai" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Eksperimentinis" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Ä®renginio podėlis" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "IÅ”valyti podėlį…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Pasenusių SMS palaikymas" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "Automatinis SFTP prijungimas" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "IÅ”plėstiniai" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "KlaviatÅ«ros trumpiniai" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "Ä®renginio nustatymai" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Suporuoti" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "Ä®renginys yra nesuporuotas" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "PrieÅ” suporuodami, galite konfigÅ«ruoti Ŕį ÄÆrenginÄÆ" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Å ifravimo informacija" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Panaikinti suporavimą" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Ä® ÄÆrenginÄÆ" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "IÅ” ÄÆrenginio" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Nieko" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Atkurti" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Sumažinti" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Nutildyti" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Nustatyti" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Norėdami atsisakyti, paspauskite Grįžimo (Esc) klaviŔą arba Naikinimo (Backspace) klaviŔą, norėdami atstatyti trumpinÄÆ." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Ä®renginio pavadinimas" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "Pe_rvadinti" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Ä®kelti iÅ” naujo" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "Mobiliųjų nustatymai" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Paslaugos meniu" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "Ä®renginio meniu" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Taisyti ÄÆrenginio pavadinimą" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Ä®renginiai" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "IeÅ”koma ÄÆrenginių…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "NarÅ”yklių priedai" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Ä®jungti" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Å is ÄÆrenginys yra nematomas nesuporuotiems ÄÆrenginiams" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Aptikimas iÅ”jungtas" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Rodinio veiksena" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Skydelis" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "Naudotojo meniu" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Generuoti palaikymo žurnalą" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "Apie GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Pasirinkti ÄÆrenginÄÆ" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Pasirinkti" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Nerasta jokių ÄÆrenginių" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Ä®renginių sąraÅ”as" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Ataskaita" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Kažkas nutiko" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "GSConnect susidÅ«rė su netikėta klaida. PraneÅ”kite apie problemą ir ÄÆtraukite bet kokią informaciją, kuri galėtų padėti." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Techninė informacija" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "Siųsti ÄÆ mobilųjÄÆ ÄÆrenginÄÆ" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Mobilieji ÄÆrenginiai" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Ä®jungti" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d prijungtas" msgstr[1] "%d prijungti" msgstr[2] "%d prijungtų" msgstr[3] "%d prijungtas" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "IÅ”jungti" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Taisyti" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Å alinti" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "IÅ”jungta" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Norėdami pakeisti %s, ÄÆveskite naują trumpinÄÆ" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s jau yra naudojamas" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Pilnas KDE Connect ÄÆgyvendinimas, skirtas GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "Moo" #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "Derinimo praneÅ”imai yra registruojami. Atlikite reikiamus veiksmus, kad pakartotumėte problemą, o tuomet peržiÅ«rėkite žurnalą." #: src/preferences/service.js:414 msgid "Review Log" msgstr "PeržiÅ«rėti žurnalą" #: src/preferences/service.js:482 msgid "Laptop" msgstr "NeÅ”iojamas kompiuteris" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "IÅ”manusis telefonas" #: src/preferences/service.js:486 msgid "Tablet" msgstr "PlanÅ”etė" #: src/preferences/service.js:488 msgid "Television" msgstr "Televizija" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Nesuporuotas" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Atjungtas" #: src/preferences/service.js:518 msgid "Connected" msgstr "Prijungtas" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "Laukiama paslaugos…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "Spustelėkite, norėdami Å”alinti nesklandumus" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "Spustelėkite iÅ”samesnei informacijai" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Rinkti numerÄÆ" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Bendrinti failą" #: src/service/daemon.js:351 msgid "List available devices" msgstr "IÅ”vardyti prieinamus ÄÆrenginius" #: src/service/daemon.js:360 msgid "List all devices" msgstr "IÅ”vardyti visus ÄÆrenginius" #: src/service/daemon.js:369 msgid "Target Device" msgstr "Paskirties ÄÆrenginys" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Pagrindinė žinutės dalis" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Siųsti praneÅ”imą" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "PraneÅ”imo programėlės pavadinimas" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Pagrindinė praneÅ”imo dalis" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "PraneÅ”imo piktograma" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "PraneÅ”imo ID" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Nuotrauka" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "RyÅ”io patikrinimas" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Rasti telefoną" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Bendrinti nuorodą" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Bendrinti tekstą" #: src/service/daemon.js:528 msgid "Show release version" msgstr "Rodyti laidos versiją" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Neprieinamas" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Bluetooth ÄÆrenginys ties %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "%s kontrolinis kodas:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Suporavimo užklausa nuo %s" #: src/service/device.js:800 msgid "Reject" msgstr "Atmesti" #: src/service/device.js:805 msgid "Accept" msgstr "Priimti" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "Aptikimas buvo iÅ”jungtas dėl Å”iame tinkle esančių ÄÆrenginių skaičiaus." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSL nerasta" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "Prievadas jau naudojamas" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "Apsikeitimas informacija apie bateriją" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: Baterija pilna" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "Pilnai ÄÆkrauta" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "%s: Baterija pasiekė tinkintą ÄÆkrovos lygÄÆ" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "%d%% ÄÆkrauta" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: Baterija baigia iÅ”sikrauti" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "Liko %d%%" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "IÅ”karpinė" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "Bendrinti iÅ”karpinės turinÄÆ" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "IÅ”siųsti iÅ”karpinės turinÄÆ" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "Gauti iÅ”kaprinės turinÄÆ" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "Gauti prieigą prie suporuotų ÄÆrenginių adresatų" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Rasti mano telefoną" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "Skambinti ÄÆ suporuotą ÄÆrenginÄÆ" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "Jutiklinis kilimėlis" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "Leidžia suporuotam ÄÆrenginiui veikti kaip nuotolinei pelei ir klaviatÅ«rai" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "KlaviatÅ«ra" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "Abikryptis nuotolinės medijos atkÅ«rimo valdymas" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "Nežinoma" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "Bendrinti praneÅ”imus su suporuotu ÄÆrenginiu" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Atsisakyti praneÅ”imo" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Užverti praneÅ”imą" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "Atsakyti ÄÆ praneÅ”imą" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Aktyvuoti praneÅ”imą" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "PraÅ”yti, kad suporuotas ÄÆrenginys padarytų nuotrauką ir perduotų ją ÄÆ Ŕį kompiuterÄÆ" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Persiuntimas nepavyko" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Nepavyko iÅ”siųsti ā€œ%sā€ ÄÆ %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "Siųsti ir gauti ryÅ”io patikrinimus" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "RyÅ”io patikrinimas: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "Pristatymas" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "Naudoti suporuotą ÄÆrenginÄÆ kaip pristatytoją" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Vykdyti komandas" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "Vykdyti komandas suporuotame ÄÆrenginyje arba leisti ÄÆrenginiui vykdyti Å”iame kompiuteryje iÅ” anksto apibrėžtas komandas" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "NarÅ”yti suporuoto ÄÆrenginio failų sistemą" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Prijungti" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Atjungti" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s praneŔė apie klaidą" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Bendrinti" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "Bendrinti failus ir URL adresus tarp ÄÆrenginių" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s neleidžiama ÄÆkelti failų" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "Persiunčiamas failas" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "Gaunama ā€ž%sā€œ iÅ” %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Persiuntimas sėkmingas" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "Gautas ā€ž%sā€œ iÅ” %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Atverti aplanką" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Atverti failą" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Nepavyko gauti ā€œ%sā€ iÅ” %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "%s bendrino tekstą" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Siunčiama ā€ž%sā€œ ÄÆ %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "IÅ”siųsta ā€ž%sā€œ ÄÆ %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Siųsti failus ÄÆ %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Užbaigus, atverti failą" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Siųsti nuorodą ÄÆ %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "Siųsti bei gauti suporuoto ÄÆrenginio SMS žinutes ir gauti praneÅ”imus apie naujas SMS" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Nauja SMS žinutė (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "Atsakyti ÄÆ SMS žinutę" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "Bendrinti SMS žinutę" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Sistemos garsumas" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "Leisti suporuotam ÄÆrenginiui valdyti sistemos garsumą" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio nerasta" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "Gauti praneÅ”imus apie skambučius ir reguliuoti sistemos garsumą skambučių metu" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Nutildyti skambutÄÆ" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Nežinomas adresatas" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Gaunamas skambutis" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Vykstantis skambutis" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Faksas" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Darbo" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Mobilusis" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Namų" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Siųsti %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Ką tik" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Vakar惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minutė" msgstr[1] "%d minutės" msgstr[2] "%d minučių" msgstr[3] "%d minutė" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Grupės žinutė" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "JÅ«s: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "Ir dar %d adresatas" msgstr[1] "Ir dar %d adresatai" msgstr[2] "Ir dar %d adresatų" msgstr[3] "Ir dar %d adresatas" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "Nuotolinė klaviatÅ«ra ties %s nėra aktyvi" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (Apskaičiuojama…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d∶%02d iki pilnos)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (Liko %d∶%02d)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Atsakyti" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Bendrinti nuorodas, naudojant GSConnect, tiesiogiai ÄÆ narÅ”yklę ar per SMS žinutę." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Paslauga neprieinama" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Atverti narÅ”yklėje" gnome-shell-extension-gsconnect-50/po/meson.build000066400000000000000000000001541421543444100222670ustar00rootroot00000000000000# build translations in LINGUAS i18n.gettext( 'org.gnome.Shell.Extensions.GSConnect', preset: 'glib' ) gnome-shell-extension-gsconnect-50/po/nl_BE.po000066400000000000000000001057541421543444100214600ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the org.gnome.Shell.Extensions.GSConnect package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: org.gnome.Shell.Extensions.GSConnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2018-11-21 19:57+0100\n" "Last-Translator: Heimen Stoffels \n" "Language-Team: Dutch \n" "Language: nl_BE\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 2.1.1\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 #, fuzzy msgid "KDE Connect implementation for GNOME" msgstr "Volledige KDE Connect-implementatie voor GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 #, fuzzy msgid "GSConnect Team" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "" "GSConnect is a complete implementation of KDE Connect especially for GNOME " "Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team " "has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "" "With GSConnect you can securely connect to mobile devices and other desktops " "to:" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 #, fuzzy msgid "Sync contacts" msgstr "Contacten" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 #, fuzzy msgid "Sync notifications" msgstr "Verzendnotificatie" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 #, fuzzy msgid "Control media players" msgstr "Mediaspelers" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 #, fuzzy msgid "Control system volume" msgstr "Systeemvolume" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Verbind met…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Annuleer" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Verbind" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "" #: data/ui/contact-chooser.ui:49 #, fuzzy msgid "No contacts" msgstr "Contacten" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Hulp" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Typ een telefoonnummer of naam" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Ander" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "Verzend SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Verzend" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Toestel is niet geconnecteerd" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 #, fuzzy msgid "Send Message" msgstr "Nieuw bericht" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Typ een bericht" #: data/ui/messaging-conversation.ui:92 #, fuzzy msgid "Message Entry" msgstr "Nieuw bericht" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Berichten" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 #, fuzzy msgid "New Conversation" msgstr "Kies een conversatie" #: data/ui/messaging-window.ui:113 #, fuzzy msgid "No Conversations" msgstr "Kies een conversatie" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "" #: data/ui/messaging-window.ui:189 #, fuzzy msgid "Select or start a conversation" msgstr "Kies een conversatie" #: data/ui/preferences-command-editor.ui:7 #, fuzzy msgid "Edit Command" msgstr "Commando's" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Naam" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Commandoregel" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Kies een uitvoerbaar bestand" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Open" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Desktop" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Klembordsynchronisatie" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Mediaspelers" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Muis en klavier" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Volumebeheer" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Bestanden" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "" #: data/ui/preferences-device-panel.ui:497 #, fuzzy msgid "Save files to" msgstr "Verzend bestanden naar %s" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Deling" #: data/ui/preferences-device-panel.ui:589 #, fuzzy msgid "Device Battery" msgstr "Accu" #: data/ui/preferences-device-panel.ui:640 #, fuzzy msgid "Low Battery Notification" msgstr "Antwoordnotificatie" #: data/ui/preferences-device-panel.ui:699 #, fuzzy msgid "Charged Up to Custom Level Notification" msgstr "Deelnotificaties" #: data/ui/preferences-device-panel.ui:779 #, fuzzy msgid "Fully Charged Notification" msgstr "Deelnotificaties" #: data/ui/preferences-device-panel.ui:833 #, fuzzy msgid "System Battery" msgstr "Accu" #: data/ui/preferences-device-panel.ui:882 #, fuzzy msgid "Share Statistics" msgstr "Deelnotificaties" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Accu" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Commando's" #: data/ui/preferences-device-panel.ui:1025 #, fuzzy msgid "Add Command" msgstr "Commando's" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Deelnotificaties" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Apps" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Notificaties" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Contacten" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Inkomende oproepen" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Volume" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Pauzeer media" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "In gesprek" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Microfoon toedoen" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Telefonie" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Actie-sneltoetsen" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Herstel alles…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Sneltoetsen" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Invoegtoepassingen" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "" #: data/ui/preferences-device-panel.ui:2000 #, fuzzy msgid "Device Cache" msgstr "Naar GSM" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "Geavanceerd" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Sneltoetsen" #: data/ui/preferences-device-panel.ui:2513 #, fuzzy msgid "Device Settings" msgstr "GSM-instellingen" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Koppel aan" #: data/ui/preferences-device-panel.ui:2589 #, fuzzy msgid "Device is unpaired" msgstr "Toestel is niet geconnecteerd" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Encryptie-info" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Koppel af" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Naar GSM" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Van GSM" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Niets" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Verlagen" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Toedoen" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Stel in" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "" "Druk op Esc om te annuleren of op Backspace om de sneltoets terug te zetten." #: data/ui/preferences-window.ui:17 #, fuzzy msgid "Device Name" msgstr "Naar GSM" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Herlaad" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "GSM-instellingen" #: data/ui/preferences-window.ui:159 #, fuzzy msgid "Service Menu" msgstr "Dienst" #: data/ui/preferences-window.ui:182 #, fuzzy msgid "Device Menu" msgstr "Naar GSM" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "" #: data/ui/preferences-window.ui:271 #, fuzzy msgid "Devices" msgstr "Naar GSM" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Browser-add-ons" #: data/ui/preferences-window.ui:626 #, fuzzy msgid "Enable" msgstr "Tablet" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Herkenning uitgeschakeld" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Weergavemodus" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Paneel" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "Gebruikersmenu" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "" #: data/ui/preferences-window.ui:749 #, fuzzy msgid "About GSConnect" msgstr "GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Kies een GSM" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Kies" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Geen toestel gevonden" #: data/ui/service-device-chooser.ui:111 #, fuzzy msgid "Device List" msgstr "Naar GSM" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Rapporteer" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "" #: data/ui/service-error-dialog.ui:84 msgid "" "GSConnect encountered an unexpected error. Please report the problem and " "include any information that may help." msgstr "" #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "Verzend naar GSM" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "GSM's" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d verbonden" msgstr[1] "%d verbonden" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Uitgeschakeld" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Voer een nieuwe sneltoets in voor het wijzigen van %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s is al in gebruik" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Volledige KDE Connect-implementatie voor GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "Heimen Stoffels" #: src/preferences/service.js:411 msgid "" "Debug messages are being logged. Take any steps necessary to reproduce a " "problem then review the log." msgstr "" #: src/preferences/service.js:414 msgid "Review Log" msgstr "" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Laptop" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Smartphone" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tablet" #: src/preferences/service.js:488 #, fuzzy msgid "Television" msgstr "Telefonie" #: src/preferences/service.js:510 #, fuzzy msgid "Unpaired" msgstr "Koppel af" #: src/preferences/service.js:514 #, fuzzy msgid "Disconnected" msgstr "Toestel is niet geconnecteerd" #: src/preferences/service.js:518 #, fuzzy msgid "Connected" msgstr "Verbind" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "Klik voor probleemoplossing" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "Klik voor meer informatie" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Telefoonoproep" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Deel bestand" #: src/service/daemon.js:351 #, fuzzy msgid "List available devices" msgstr "Onbeschikbaar" #: src/service/daemon.js:360 #, fuzzy msgid "List all devices" msgstr "GSM's" #: src/service/daemon.js:369 #, fuzzy msgid "Target Device" msgstr "Naar GSM" #: src/service/daemon.js:411 #, fuzzy msgid "Message Body" msgstr "Nieuw bericht" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Verzendnotificatie" #: src/service/daemon.js:432 #, fuzzy msgid "Notification App Name" msgstr "Notificaties" #: src/service/daemon.js:441 #, fuzzy msgid "Notification Body" msgstr "Notificaties" #: src/service/daemon.js:450 #, fuzzy msgid "Notification Icon" msgstr "Notificaties" #: src/service/daemon.js:459 #, fuzzy msgid "Notification ID" msgstr "Notificaties" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 #, fuzzy msgid "Ring" msgstr "Lokaliseer" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Deel link" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Deel tekst" #: src/service/daemon.js:528 #, fuzzy msgid "Show release version" msgstr "Kies een conversatie" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Onbeschikbaar" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Bluetooth-toestel op %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "%s-vingerafdruk:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Koppelaanvraag van %s" #: src/service/device.js:800 msgid "Reject" msgstr "Verwerp" #: src/service/device.js:805 msgid "Accept" msgstr "Accepteer" #: src/service/manager.js:114 msgid "" "Discovery has been disabled due to the number of devices on this network." msgstr "" "Herkenning is uitgeschakeld vanwege het aantal toestellen op dit netwerk." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "" #: src/service/backends/lan.js:452 #, fuzzy msgid "Port already in use" msgstr "%s is al in gebruik" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, fuzzy, javascript-format msgid "%s: Battery is full" msgstr "%s: batterij is laag" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "Volledig opgeladen" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, fuzzy, javascript-format msgid "%d%% Charged" msgstr "Volledig opgeladen" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: batterij is laag" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% resterend" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Klembord" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "Verstuur klembord" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "Haal klembord op" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Vind mijn telefoon" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "Touchpad" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Klavier" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 #, fuzzy msgid "Unknown" msgstr "Onbekend contact" #: src/service/plugins/notification.js:16 #, fuzzy msgid "Share notifications with the paired device" msgstr "Geen GSM-notificaties" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Annuleer notificatie" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Sluit notificatie" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "Antwoordnotificatie" #: src/service/plugins/notification.js:62 #, fuzzy msgid "Activate Notification" msgstr "Deelnotificaties" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Overzetten niet gelukt" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Kon ā€œ%sā€ niet verzenden naar %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 #, fuzzy msgid "Presentation" msgstr "Bestanden-integratie" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Voer commando's uit" #: src/service/plugins/runcommand.js:13 msgid "" "Run commands on your paired device or let the device run predefined commands " "on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Koppel aan" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Koppel af" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Deel" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 #, fuzzy msgid "Transferring File" msgstr "Overzetten niet gelukt" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "ā€œ%sā€ ontvangen van %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Met succes overgezet" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "ā€œ%sā€ ontvangen van %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Open folder" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Open bestand" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Kon ā€œ%sā€ niet ontvangen van %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Tekst, gedeeld door %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "ā€œ%sā€ verzenden naar %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "ā€œ%sā€ verzonden naar %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Verzend bestanden naar %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 #, fuzzy msgid "Open when done" msgstr "Open in browser" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Verzend een link naar %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Nieuwe SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "Beantwoord SMS" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "Deel SMS" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Systeemvolume" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 #, fuzzy msgid "PulseAudio not found" msgstr "PulseAudio-fout" #: src/service/plugins/telephony.js:14 msgid "" "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Demp oproep" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Onbekend contact" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Inkomende oproep" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "In gesprek" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Fax" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Job" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "GSM" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Thuis" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Verzend naar %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Zopas" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Gisteren - %s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minuut" msgstr[1] "%d minuten" #: src/service/ui/messaging.js:750 #, fuzzy msgid "Group Message" msgstr "Nieuw bericht" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "" msgstr[1] "" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (berekenen...)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d%02d tot volledig opgeladen)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d%02d resterend)" #: src/shell/notification.js:54 #, fuzzy msgid "Reply" msgstr "Beantwoord SMS" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Deel links met GSConnect, direct naar de browser of per SMS." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Dienst onbeschikbaar" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Open in browser" #~ msgid "On" #~ msgstr "Aan" #~ msgid "Off" #~ msgstr "Uit" #~ msgid "Set Shortcut" #~ msgstr "Stel sneltoets in" #~ msgid "Authentication Failure" #~ msgstr "Authenticatieprobleem" #~ msgid "Network Error" #~ msgstr "Netwerkfout" #~ msgid "Keyboard not ready" #~ msgstr "Klavier niet gereed" #~ msgid "All files" #~ msgstr "Alle bestanden" #~ msgid "Camera pictures" #~ msgstr "Camera-afbeeldingen" #, fuzzy, javascript-format #~ msgid "%d hour" #~ msgid_plural "%d hours" #~ msgstr[0] "EĆ©n uur" #~ msgstr[1] "%d uur" #, javascript-format #~ msgid "Until %s (%s)" #~ msgstr "Tot %s (%s)" #~ msgid "Do Not Disturb" #~ msgstr "Stoor mij niet" #~ msgid "Until you turn off Do Not Disturb" #~ msgstr "Totdat je Stoor mij niet uitschakelt" #~ msgid "Done" #~ msgstr "Klaar" #~ msgid "Command Shortcuts" #~ msgstr "Commando-sneltoetsen" #~ msgid "Delete" #~ msgstr "Verwijder" #~ msgid "Delete this device" #~ msgstr "Verwijder deze GSM" #~ msgid "Unpair and remove all settings and files" #~ msgstr "Koppel af en wis alle instellingen en bestanden" #~ msgid "Debugger" #~ msgstr "Foutopsporing" #~ msgid "About" #~ msgstr "Over" #~ msgid "Switch to Bluetooth" #~ msgstr "Schakel naar Bluetooth" #~ msgid "Switch to LAN" #~ msgstr "Schakel naar LAN" #~ msgid "Appearance" #~ msgstr "Uiterlijk" #~ msgid "Discoverable" #~ msgstr "Zichtbaar" #~ msgid "Restart Service" #~ msgstr "Herstart dienst" #~ msgid "Settings" #~ msgstr "Instellingen" #~ msgid "Remote Filesystems" #~ msgstr "Externe bestandssystemen" #~ msgid "Sound Effects" #~ msgstr "Geluidseffecten" #~ msgid "Extended Keyboard Support" #~ msgstr "Uitgebreide klavierondersteuning" #~ msgid "Desktop Contacts" #~ msgstr "Bureaubladcontacten" #~ msgid "Additional Features" #~ msgstr "Additionele functionaliteiten" #~ msgid "KDE Connect" #~ msgstr "KDE Connect" #~ msgid "Click to open preferences" #~ msgstr "Klik om voorkeuren te openen" #~ msgid "Additional Software Required" #~ msgstr "Additionele software vereist" #~ msgid "%s Plugin Failed To Load" #~ msgstr "Kon plug-in %s niet laden" #~ msgid "Wayland Not Supported" #~ msgstr "Wayland niet ondersteund" #~ msgid "Remote input not supported on Wayland" #~ msgstr "Externe input wordt niet ondersteund op Wayland" #~ msgid "GSConnect: %s" #~ msgstr "GSConnect: %s" #~ msgid "Reconnect" #~ msgstr "Herverbinden" #~ msgid "Locate Device" #~ msgstr "Lokaliseer GSM" #~ msgid "%s asked to locate this device" #~ msgstr "%s wil deze GSM lokaliseren" #~ msgid "Found" #~ msgstr "Gevonden" #~ msgid "Starting Transfer" #~ msgstr "Overzetten starten" #~ msgid "Select a contact or number" #~ msgstr "Kies een contact of nummer" gnome-shell-extension-gsconnect-50/po/nl_NL.po000066400000000000000000001130641421543444100214740ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the org.gnome.Shell.Extensions.GSConnect package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: org.gnome.Shell.Extensions.GSConnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-10-04 16:59+0200\n" "Last-Translator: Heimen Stoffels \n" "Language-Team: Dutch \n" "Language: nl_NL\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.0\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "KDE Connect-implementatie voor GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "GSConnect-team" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "" "GSConnect is a complete implementation of KDE Connect especially for GNOME " "Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team " "has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "" "GSConnect is een volledige implementatie van KDE Connect voor GNOME Shell, " "met Nautilus-, Chrome- en Firefox-integratie. Het KDE Connect-team biedt " "clients aan voor Linux, BSD, Android, Sailfish OS, macOS en Windows." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "" "With GSConnect you can securely connect to mobile devices and other desktops " "to:" msgstr "" "Met GSConnect kun je veilig verbinden met mobiele apparaten en computers om:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Bestanden, links en tekst te delen" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Berichten te versturen en ontvangen" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "De klembordinhoud te synchroniseren" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Contactpersonen te synchroniseren" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Meldingen te synchroniseren" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Mediaspelers te bedienen" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "Het volumeniveau van het systeem aan te passen" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Opgegeven opdrachten uit te voeren" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "En nog veel meer…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect in GNOME Shell" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Verbinden met…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Annuleren" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Verbinden" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP-adres" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "Contactpersonen" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Hulp" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Typ een telefoonnummer of naam" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Overig" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "SMS versturen" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Versturen" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Apparaat is niet verbonden" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Bericht versturen" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Typ een bericht" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "Berichtinhoud" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Typ een bericht en druk op enter om te versturen" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Berichten" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Nieuw gesprek" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Geen gesprekken" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Geen gesprek gekozen" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Kies of begin een gesprek" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Opdracht aanpassen" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Opslaan" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Naam" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Opdrachtregel" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Kies een uitvoerbaar bestand" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Openen" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Computer" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "Camera" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Klembordsynchronisatie" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Mediaspelers" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Muis en toetsenbord" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Volumebeheer" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Bestanden" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Bestanden ontvangen" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Bestanden opslaan in" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Delen" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Apparaataccu" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Melding bij laag accuniveau" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "Melding bij ingesteld accuniveau" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Melding bij 100% accuniveau" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Systeemaccu" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Statistieken delen" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Accu" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Opdrachten" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Opdracht toevoegen" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Meldingen omtrent delen" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Delen indien actief" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Toepassingen" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Meldingen" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Contactpersonen" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Inkomende oproepen" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Volume" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Media onderbreken" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Actieve gesprekken" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Microfoon dempen" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Telefoon" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Actiesneltoetsen" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Standaardwaarden…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Sneltoetsen" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Plug-ins" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Experimenteel" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Apparaatcache" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Cache legen…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Verouderde sms-ondersteuning" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "SFTP automatisch aankoppelen" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "Geavanceerd" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Sneltoetsen" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "Apparaatinstellingen" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Koppelen" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "Apparaat is niet gekoppeld" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Je kunt dit apparaat instellen alvorens het te koppelen" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Versleutelingsinformatie" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Ontkoppelen" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Naar apparaat" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Van apparaat" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Niets" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Herstellen" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Verlagen" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Dempen" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Instellen" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "" "Druk op Esc om te annuleren of Backspace om de standaard sneltoets te " "herstellen." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Apparaatnaam" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_Naam wijzigen" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Verversen" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "Mobiele instellingen" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Dienstmenu" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "Apparaatmenu" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Apparaatnaam wijzigen" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Apparaten" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Bezig met zoeken naar apparaten…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Browseradd-ons" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Inschakelen" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Dit apparaat is onzichtbaar voor niet-gekoppelde apparaten" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Herkenning uitgeschakeld" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Weergavemodus" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Paneel" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "Gebruikersmenu" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Ondersteuningslogboek samenstellen" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "Over GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Kies een apparaat" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Kiezen" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Geen apparaat gevonden" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Apparatenlijst" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Melden" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Er is iets misgegaan" #: data/ui/service-error-dialog.ui:84 msgid "" "GSConnect encountered an unexpected error. Please report the problem and " "include any information that may help." msgstr "" "Er is een onverwachte fout opgetreden. Meld dit probleem aan de ontwikkelaar " "en beschrijf wat je deed ten tijde van de fout." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Technische gegevens" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "Versturen naar mobiel apparaat" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Mobiele apparaten" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Inschakelen" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d verbonden" msgstr[1] "%d verbonden" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Uitschakelen" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Bewerken" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Verwijderen" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Uitgeschakeld" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Druk op een nieuwe sneltoets voor %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s is al in gebruik" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Een volledige KDE Connect-implementatie voor GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "Heimen Stoffels " #: src/preferences/service.js:411 msgid "" "Debug messages are being logged. Take any steps necessary to reproduce a " "problem then review the log." msgstr "" "Foutopsporingsberichten worden gelogd. Doe alles wat nodig is om het " "probleem aan te tonen en kijk dan het logboek na." #: src/preferences/service.js:414 msgid "Review Log" msgstr "Logboek nakijken" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Laptop" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Smartphone" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tablet" #: src/preferences/service.js:488 msgid "Television" msgstr "Televisie" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Ontkoppeld" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Niet verbonden" #: src/preferences/service.js:518 msgid "Connected" msgstr "Verbonden" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "Bezig met wachten op dienst…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "Klik voor probleemoplossing" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "Klik voor meer informatie" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Nummer bellen" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Bestand delen" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Beschikbare apparaten tonen" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Alle apparaten tonen" #: src/service/daemon.js:369 msgid "Target Device" msgstr "Doelapparaat" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Berichtinhoud" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Melding versturen" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "Appnaam op melding" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Meldingsinhoud" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Meldingspictogram" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "Meldingsid" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Foto" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Over laten gaan" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Link delen" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Tekst delen" #: src/service/daemon.js:528 msgid "Show release version" msgstr "Uitgaveversie tonen" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Niet beschikbaar" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Bluetoothapparaat op %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "%s-vingerafdruk:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Koppelverzoek van %s" #: src/service/device.js:800 msgid "Reject" msgstr "Weigeren" #: src/service/device.js:805 msgid "Accept" msgstr "Accepteren" #: src/service/manager.js:114 msgid "" "Discovery has been disabled due to the number of devices on this network." msgstr "" "Herkenning is uitgeschakeld vanwege het aantal apparaten op dit netwerk." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSL is niet aangetroffen" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "Deze poort is al in gebruik" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "Accu-informatie uitwisselen" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: de accu is opgeladen" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "Volledig opgeladen" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "%s: de accu heeft het ingestelde oplaadniveau bereikt" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "%d%% opgeladen" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: het accuniveau is laag" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% resterend" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Klembord" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "Klembordinhoud delen" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "Klembord versturen" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "Klembord ophalen" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "Toegang tot contactpersonen van het gekoppelde apparaat" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Zoek mijn telefoon" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "Gekoppeld apparaat laten rinkelen" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "Touchpad" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "Laat het gekoppelde apparaat fungeren als externe muis en toetsenbord" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Toetsenbord" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "Media twee kanten op bedienen" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "Onbekend" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "Meldingen delen met het gekoppelde apparaat" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Melding annuleren" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Melding sluiten" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "Antwoordmelding" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Melding inschakelen" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" "Verzoek het gekoppelde apparaat een foto te maken en deze naar de pc te " "sturen" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Overdracht mislukt" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Versturen van ā€œ%sā€ naar %s mislukt" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "Pings ontvangen en versturen" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "Presentatie" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "Gekoppeld apparaat laten fungeren als presentatiescherm" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Opdrachten uitvoeren" #: src/service/plugins/runcommand.js:13 msgid "" "Run commands on your paired device or let the device run predefined commands " "on this PC" msgstr "" "Voer opdrachten uit op het gekoppelde apparaat of laat het apparaat vooraf " "ingestelde opdrachten uitvoeren op deze pc" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "Bladeren door het bestandssysteem van het gekoppelde apparaat" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Aankoppelen" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Ontkoppelen" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s: er is een fout opgetreden" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Delen" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "Bestanden en url's uitwisselen tussen apparaten" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s mag geen bestanden sturen" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "Bestandsoverdracht" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "Bezig met ontvangen van ā€œ%sā€ van %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Overdracht voltooid" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "ā€œ%sā€ ontvangen van %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Map openen" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Bestand openen" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Ontvangen van ā€œ%sā€ van %s mislukt" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Tekst van %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Bezig met versturen van ā€œ%sā€ naar %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "ā€œ%sā€ is verstuurd naar %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Bestanden versturen naar %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Openen na overdracht" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Link versturen naar %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" "Lees en verstuur sms'jes van het gekoppelde apparaat en ontvang sms-meldingen" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Nieuwe sms (uri)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "SMS beantwoorden" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "SMS delen" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Volumeniveau van systeem" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "Laat het gekoppelde apparaat het systeemvolume aanpassen" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio is niet aangetroffen" #: src/service/plugins/telephony.js:14 msgid "" "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" "Ontvang meldingen omtrent oproepen en volume-aanpassingen tijdens rinkelen/" "actieve gesprekken" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Oproep dempen" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Onbekende contactpersoon" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Inkomende oproep" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Actief gesprek" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Fax" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Werk" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Mobiel" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Thuis" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Versturen naar %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Zojuist" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Gisteren - %s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minuut" msgstr[1] "%d minuten" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Groepsbericht" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Ik: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "En %d andere contactpersoon" msgstr[1] "En %d anderen" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "Het externe toetsenbord op '%s' is inactief" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (bezig met berekenen…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d:%02d tot volledig opgeladen)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d:%02d resterend)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Beantwoorden" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Deel links met GSConnect, direct naar de browser of via sms." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Dienst niet beschikbaar" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Openen in browser" #~ msgid "On" #~ msgstr "Aan" #~ msgid "Off" #~ msgstr "Uit" #~ msgid "Network Error" #~ msgstr "Netwerkfout" #~ msgid "All files" #~ msgstr "Alle bestanden" #~ msgid "Camera pictures" #~ msgstr "Camera-afbeeldingen" #~ msgid "Add" #~ msgstr "Toevoegen" #~ msgid "Set Shortcut" #~ msgstr "Sneltoets instellen" #~ msgid "Authentication Failure" #~ msgstr "Authenticatiefout" #~ msgid "Keyboard not ready" #~ msgstr "Toetsenbord niet gereed" #~ msgid "%d hour" #~ msgid_plural "%d hours" #~ msgstr[0] "%d uur" #~ msgstr[1] "%d uur" #~ msgid "Until %s (%s)" #~ msgstr "Tot %s (%s)" #~ msgid "Do Not Disturb" #~ msgstr "Niet storen" #~ msgid "Until you turn off Do Not Disturb" #~ msgstr "Totdat je Niet storen uitschakelt" #~ msgid "Done" #~ msgstr "Klaar" #~ msgid "Bluetooth Device" #~ msgstr "Bluetooth-apparaat" #~ msgid "Command Shortcuts" #~ msgstr "Opdracht-sneltoetsen" #~ msgid "Delete" #~ msgstr "Verwijderen" #~ msgid "Delete this device" #~ msgstr "Dit apparaat verwijderen" #~ msgid "Unpair and remove all settings and files" #~ msgstr "Ontkoppelen en alle instellingen en bestanden verwijderen" #~ msgid "Switch to Bluetooth" #~ msgstr "Overschakelen naar Bluetooth" #~ msgid "Switch to LAN" #~ msgstr "Overschakelen naar LAN" #~ msgid "%s Plugin Failed To Load" #~ msgstr "Kan plug-in %s niet laden" #~ msgid "Reconnect" #~ msgstr "Opnieuw verbinden" #~ msgid "Settings" #~ msgstr "Instellingen" #~ msgid "Additional Software Required" #~ msgstr "Extra software vereist" #~ msgid "Starting Transfer" #~ msgstr "Bezig met starten van overdracht" #~ msgid "" #~ "Transferred files are placed in the Downloads folder." #~ msgstr "" #~ "Overgedragen bestanden worden geplaatst in de map Downloads." #~ msgid "About" #~ msgstr "Over" #~ msgid "KDE Connect" #~ msgstr "KDE Connect" #~ msgid "Debugger" #~ msgstr "Foutopsporing" #~ msgid "Appearance" #~ msgstr "Uiterlijk" #~ msgid "Discoverable" #~ msgstr "Zichtbaar" #~ msgid "Restart Service" #~ msgstr "Dienst herstarten" #~ msgid "Remote Filesystems" #~ msgstr "Externe bestandssystemen" #~ msgid "Sound Effects" #~ msgstr "Geluidseffecten" #~ msgid "Extended Keyboard Support" #~ msgstr "Uitgebreide toetsenbordondersteuning" #~ msgid "Desktop Contacts" #~ msgstr "Bureaubladcontactpersonen" #~ msgid "Additional Features" #~ msgstr "Extra functies" #~ msgid "Click to open preferences" #~ msgstr "Klik om voorkeuren te openen" #~ msgid "Wayland Not Supported" #~ msgstr "Geen Wayland-ondersteuning" #~ msgid "Remote input not supported on Wayland" #~ msgstr "Externe invoer wordt niet ondersteund op Wayland" #~ msgid "GSConnect: %s" #~ msgstr "GSConnect: %s" #~ msgid "Locate Device" #~ msgstr "Apparaat lokaliseren" #~ msgid "%s asked to locate this device" #~ msgstr "%s wil dit apparaat lokaliseren" #~ msgid "Found" #~ msgstr "Gevonden" #~ msgid "Select a contact or number" #~ msgstr "Kies een contactpersoon of telefoonnummer" gnome-shell-extension-gsconnect-50/po/org.gnome.Shell.Extensions.GSConnect.pot000066400000000000000000000716341421543444100276050ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the org.gnome.Shell.Extensions.GSConnect package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: org.gnome.Shell.Extensions.GSConnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "" "GSConnect is a complete implementation of KDE Connect especially for GNOME " "Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team " "has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "" "With GSConnect you can securely connect to mobile devices and other desktops " "to:" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "" #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "" #: data/ui/service-error-dialog.ui:84 msgid "" "GSConnect encountered an unexpected error. Please report the problem and " "include any information that may help." msgstr "" #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "" msgstr[1] "" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "" #: src/preferences/service.js:411 msgid "" "Debug messages are being logged. Take any steps necessary to reproduce a " "problem then review the log." msgstr "" #: src/preferences/service.js:414 msgid "Review Log" msgstr "" #: src/preferences/service.js:482 msgid "Laptop" msgstr "" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "" #: src/preferences/service.js:486 msgid "Tablet" msgstr "" #: src/preferences/service.js:488 msgid "Television" msgstr "" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "" #: src/preferences/service.js:518 msgid "Connected" msgstr "" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "" #: src/service/daemon.js:351 msgid "List available devices" msgstr "" #: src/service/daemon.js:360 msgid "List all devices" msgstr "" #: src/service/daemon.js:369 msgid "Target Device" msgstr "" #: src/service/daemon.js:411 msgid "Message Body" msgstr "" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "" #: src/service/daemon.js:528 msgid "Show release version" msgstr "" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "" #: src/service/device.js:800 msgid "Reject" msgstr "" #: src/service/device.js:805 msgid "Accept" msgstr "" #: src/service/manager.js:114 msgid "" "Discovery has been disabled due to the number of devices on this network." msgstr "" #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "" #: src/service/plugins/runcommand.js:13 msgid "" "Run commands on your paired device or let the device run predefined commands " "on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "" #: src/service/plugins/telephony.js:14 msgid "" "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "" msgstr[1] "" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "" msgstr[1] "" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "" #: src/shell/notification.js:54 msgid "Reply" msgstr "" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "" #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "" gnome-shell-extension-gsconnect-50/po/pl.po000066400000000000000000001076261421543444100211140ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-09 01:50\n" "Last-Translator: \n" "Language-Team: Polish\n" "Language: pl_PL\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: pl\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "Implementacja KDE Connect dla środowiska GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "Zespół GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnect to pełna implementacja KDE Connect specjalnie dla Powłoki GNOME zĀ integracją zĀ programami Nautilus, Chrome iĀ Firefox. Zespół KDE Connect ma aplikacje dla systemów Linux, BSD, Android, Sailfish, macOS iĀ Windows." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "Za pomocą GSConnect można bezpiecznie połączyć się zĀ telefonem iĀ innymi komputerami, aby:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Udostępniać pliki, odnośniki iĀ teksty" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Wysyłać iĀ odbierać wiadomości" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Synchronizować zawartość schowka" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Synchronizować kontakty" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Synchronizować powiadomienia" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Sterować odtwarzaczami multimedialnymi" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "Sterować głośnością systemową" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Wykonywać wcześniej określone polecenia" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "IĀ wiele więcej…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect wĀ Powłoce GNOME" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Połącz z…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Anuluj" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Połącz" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "Adres IP" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "Brak kontaktów" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Pomoc" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Numer telefonu lub nazwa kontaktu" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Inny" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "Wyślij SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Wyślij" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Urządzenie jest rozłączone" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Wysyła wiadomość" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Napisz wiadomość" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "Pole wpisywania wiadomości" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Proszę wpisać wiadomość iĀ nacisnąć klawisz Enter, aby ją wysłać" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Wiadomości" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Nowa rozmowa" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Brak rozmów" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Nie wybrano rozmowy" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Wybierz lub rozpocznij rozmowę" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Modyfikacja polecenia" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Zapisuje" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Nazwa" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Wiersz poleceń" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Wybiera plik wykonywalny" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Otwórz" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Komputer stacjonarny" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "Aparat" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Synchronizacja schowka" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Odtwarzacze multimediów" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Mysz iĀ klawiatura" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Sterowanie głośnością" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Pliki" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Odbieranie plików" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Zapisywanie plików w" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Udostępnianie" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Akumulator urządzenia" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Powiadomienie oĀ niskim poziomie naładowania akumulatora" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "Powiadomienie oĀ naładowaniu do danego poziomu" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Powiadomienie oĀ pełnym naładowaniu" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Akumulator komputera" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Udostępnianie statystyk" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Akumulator" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Polecenia" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Dodaj polecenie" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Udostępnianie powiadomień" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Udostępnianie podczas aktywności" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Programy" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Powiadomienia" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Kontakty" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Połączenia przychodzące" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Głośność" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Wstrzymywanie multimediów" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Trwające połączenia" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Wyciszanie mikrofonu" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Komunikacja" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Skróty działań" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Przywróć wszystko…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Skróty" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Wtyczki" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Eksperymentalne" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Pamięć podręczna urządzenia" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Wyczyść pamięć podręczną…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Obsługa SMS (poprzednia wersja)" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "Automatyczne montowanie SFTP" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "Zaawansowane" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Skróty klawiszowe" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "Ustawienia urządzenia" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Powiąż" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "Urządzenie jest niepowiązane" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Można skonfigurować to urządzenie przed powiązaniem" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Informacje oĀ szyfrowaniu" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Odwiąż" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Do urządzenia" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "ZĀ urządzenia" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Nic" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Przywróć" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Ciszej" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Wycisz" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Ustaw" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Klawisz Esc anuluje, aĀ Backspace przywróci skrót klawiszowy." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Nazwa urządzenia" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_Zmień nazwę" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Odśwież" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "Ustawienia urządzeń mobilnych" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Menu usługi" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "Menu urządzenia" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Modyfikuje nazwę urządzenia" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Urządzenia" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Wyszukiwanie urządzeń…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Dodatki do przeglądarek" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Włącz" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "To urządzenie jest niewidoczne dla niepowiązanych urządzeń" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Wykrywanie jest wyłączone" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Tryb wyświetlania" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Panel" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "Menu użytkownika" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Utwórz dziennik wsparcia" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "OĀ programie" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Wybór urządzenia" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Wybierz" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Nie odnaleziono żadnego urządzenia" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Lista urządzeń" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Zgłoś" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Coś się nie powiodło" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "Wystąpił nieoczekiwany błąd wĀ GSConnect. Proszę go zgłosić iĀ dołączyć wszelkie informacje, które mogą pomóc." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Informacje techniczne" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "Wyślij na urządzenie mobilne" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Urządzenia mobilne" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Włącz" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d połączone urządzenie" msgstr[1] "%d połączone urządzenia" msgstr[2] "%d połączonych urządzeń" msgstr[3] "%d połączonych urządzeń" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Wyłącz" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Modyfikuje" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Usuwa" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Wyłączone" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Proszę wprowadzić nowy skrót, aby zmienić ā€ž%sā€" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s jest już używane" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Pełna implementacja KDE Connect dla środowiska GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "Adrian Kryński , 2017\n" "Piotr Drąg , 2018-2020\n" "Aviary.pl , 2018-2020" #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "Komunikaty debugowania są zapisywane wĀ dzienniku. Proszę podjąć działania niezbędne do powtórzenia problemu, aĀ następnie przejrzeć dziennik." #: src/preferences/service.js:414 msgid "Review Log" msgstr "Przejrzyj dziennik" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Laptop" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Smartfon" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tablet" #: src/preferences/service.js:488 msgid "Television" msgstr "Telewizor" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Niepowiązane" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Rozłączone" #: src/preferences/service.js:518 msgid "Connected" msgstr "Połączone" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "Oczekiwanie na usługę…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "Uzyskaj pomoc wĀ rozwiązaniu problemu" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "Więcej informacji" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Zadzwoń" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Udostępnij plik" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Wyświetla listę dostępnych urządzeń" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Wyświetla listę wszystkich urządzeń" #: src/service/daemon.js:369 msgid "Target Device" msgstr "Urządzenie docelowe" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Treść wiadomości" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Wyślij powiadomienie" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "Nazwa aplikacji powiadomienia" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Treść powiadomienia" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Ikona powiadomienia" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "Identyfikator powiadomienia" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Zdjęcie" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Zadzwoń" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Udostępnij odnośnik" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Udostępnij tekst" #: src/service/daemon.js:528 msgid "Show release version" msgstr "Wyświetla wersję wydania" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Niedostępne" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Urządzenie Bluetooth pod adresem %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "Odcisk urządzenia %s:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Prośba oĀ powiązanie zĀ urządzenia %s" #: src/service/device.js:800 msgid "Reject" msgstr "Odrzuć" #: src/service/device.js:805 msgid "Accept" msgstr "Przyjmij" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "Wykrywanie zostało wyłączone zĀ powodu liczby urządzeń wĀ tej sieci." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "Nie odnaleziono biblioteki OpenSSL" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "Port jest już używany" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "Wymiana informacji oĀ naładowaniu akumulatora" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: akumulator jest wĀ pełni naładowany" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "WĀ pełni naładowane" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "%s: akumulator został naładowany do danego poziomu" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "Naładowano %d%%" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: poziom naładowania akumulatora jest niski" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "Pozostało %d%%" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Schowek" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "Udostępnianie zawartości schowka" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "Wysyłanie do schowka" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "Odbieranie ze schowka" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "Dostęp do kontaktów powiązanego urządzenia" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "ZnajdÅŗ mój telefon" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "Dzwonienie na powiązane urządzenie" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "Podkładka pod mysz" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "Umożliwienie używania powiązanego urządzenia jako zdalnej myszy iĀ klawiatury" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Klawiatura" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "Dwukierunkowe zdalne sterowanie odtwarzaniem multimediów" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "Nieznany" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "Udostępnianie powiadomień powiązanemu urządzeniu" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Anuluj powiadomienie" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Zamknij powiadomienie" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "Odpowiedz na powiadomienie" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Aktywuj powiadomienie" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "Powiązane urządzenie robi zdjęcie iĀ przesyła je do komputera" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Przesłanie się nie powiodło" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Wysłanie ā€ž%sā€ do urządzenia %s się nie powiodło" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "Wysyłanie iĀ odbieranie sygnałów ping" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "Prezentacja" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "Używanie powiązanego urządzenia jako prezentera" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Wykonywanie poleceń" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "Wykonywanie poleceń na powiązanym urządzeniu lub umożliwienie urządzeniu wykonywania wcześniej ustalonych poleceń na tym komputerze" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "Przeglądanie systemu plików powiązanego urządzenia" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Zamontuj" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Odmontuj" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "Urządzenie %s zgłosiło błąd" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Udostępnij" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "Udostępnianie plików iĀ adresów URL między urządzeniami" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "Urządzenie %s nie może wysyłać plików" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "Przesyłanie pliku" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "Odbieranie ā€ž%sā€ zĀ urządzenia %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Pomyślnie przesłano" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "Odebrano ā€ž%sā€ zĀ urządzenia %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Otwórz katalog" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Otwórz plik" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Odebranie ā€ž%sā€ zĀ urządzenia %s się nie powiodło" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Tekst udostępniony przez: %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Wysyłanie ā€ž%sā€ do urządzenia %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "Wysłano ā€ž%sā€ do urządzenia %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Wysłanie plików do urządzenia %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Otwarcie po ukończeniu" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Wysyła odnośnik do urządzenia %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "Wysyłanie iĀ odczytywanie wiadomości SMS powiązanego urządzenia iĀ powiadamianie oĀ nowych SMS-ach" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Nowy SMS (adres URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "Odpowiedz na SMS" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "Udostępnij SMS" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Głośność systemu" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "Umożliwienie powiązanemu urządzeniu sterowania głośnością systemu" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "Nie odnaleziono usługi PulseAudio" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "Powiadamianie oĀ połączeniach iĀ dostosowywanie głośności systemu podczas oczekujących/trwających połączeń" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Wycisz połączenie" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Nieznany kontakt" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Połączenie przychodzące" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Trwające połączenie" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Faks" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Służbowy" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Komórkowy" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Domowy" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Wyślij do ā€ž%sā€" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Przed chwilą" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Wczoraj惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minuta" msgstr[1] "%d minuty" msgstr[2] "%d minut" msgstr[3] "%d minut" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Wiadomość grupowa" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Ja: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "IĀ %d inny kontakt" msgstr[1] "IĀ %d inne kontakty" msgstr[2] "IĀ %d innych kontaktów" msgstr[3] "IĀ %d innych kontaktów" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "Zdalna klawiatura na urządzeniu %s nie jest aktywna" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (obliczanie…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (do naładowania: %d∶%02d)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (pozostało: %d∶%02d)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Odpowiedz" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Udostępnianie odnośników za pomocą GSConnect, bezpośrednio do przeglądarki lub przez wiadomość SMS." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Usługa jest niedostępna" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Otwórz wĀ przeglądarce" gnome-shell-extension-gsconnect-50/po/pt-PT.po000066400000000000000000001056111421543444100214350ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-16 15:42\n" "Last-Translator: \n" "Language-Team: Portuguese\n" "Language: pt_PT\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: pt-PT\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "Implementação do KDE Connect para o GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "Equipa do GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "O GSConnect Ć© uma implementação completa do KDE Connect especialmente para a GNOME Shell com integração Nautilus, Chrome e Firefox. A equipa do KDE Connect tem aplicaƧƵes para Linux, BSD, Android, Sailfish, macOS e Windows." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "Com o GSConnect pode ligar-se de forma segura a dispositivos móveis e outros ambientes de trabalho para:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Partilhar ficheiros, ligaƧƵes e texto" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Enviar e receber mensagens" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Sinconizar conteĆŗdo da Ć”rea de transferĆŖncia" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Sincronizar contactos" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Sincronizar notificaƧƵes" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Controlar leitores multimĆ©dia" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "Controlar volume do sistema" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Executar comandos predefinidos" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "E mais…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect na GNOME Shell" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Ligar a…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Cancelar" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Ligar" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "EndereƧo de IP" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "Nenhum contacto" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Ajuda" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Introduza um nĆŗmero de telefone ou nome" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Outros" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "Enviar SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Enviar" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "O dispositivo estĆ” desligado" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Enviar mensagem" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Escreva uma mensagem" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "Entrada de mensagens" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Escreva uma mensagem e prima Enter para enviar" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Mensagens" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Nova conversa" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Sem conversas" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Nenhuma conversa selecionada" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Selecionar ou iniciar uma conversa" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Editar comando" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Guardar" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Nome" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Linha de comandos" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Escolha um executĆ”vel" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Abrir" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Ɓrea de trabalho" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "CĆ¢mara" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Sincronizar a Ć”rea de transferĆŖncia" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Leitores multimĆ©dia" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Rato e Teclado" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Controlo de volume" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Ficheiros" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Receber ficheiros" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Guardar ficheiros para" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Partilhar" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Bateria do dispositivo" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Notificação de bateria fraca" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "Carregado atĆ© Ć  notificação de nĆ­vel personalizada" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Notificação de carga completa" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Bateria do sistema" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Partilhar estatĆ­sticas" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Bateria" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Comandos" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Adicionar comando" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Partilhar notificaƧƵes" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Partilhar quando ativo" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "AplicaƧƵes" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "NotificaƧƵes" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Contactos" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Chamadas recebidas" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Volume" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Colocar em pausa" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Chamadas em curso" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Silenciar microfone" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Telefonia" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Atalhos de ação" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Repor tudo…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Atalhos" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "ExtensƵes" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Experimental" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Cache do dispositivo" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Limpar cache…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Suporte por SMS antigo" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "Montagem automĆ”tica SFTP" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "AvanƧado" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Teclas de atalho" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "DefiniƧƵes do dispositivo" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Emparelhar" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "O dispositivo nĆ£o estĆ” emparelhado" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Pode configurar este dispositivo antes de emparelhar" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Informação de encriptação" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Desemparelhar" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Para o dispositivo" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Do dispositivo" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Nada" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Restaurar" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Baixo" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Silenciar" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Definir" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Prima Esc para cancelar ou Backspace para repor a tecla de atalho." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Nome do dispositivo" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_Mudar o nome" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Recarregar" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "DefiniƧƵes móveis" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Menu de serviƧo" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "Menu de dispositivos" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Editar nome do dispositivo" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Dispositivos" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "A procurar por dispositivos…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Add-Ons do navegador" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Ativar" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Este dispositivo Ć© invisĆ­vel a dispositivos nĆ£o emparelhados" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Detecção desativada" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Modo de visualização" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Painel" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "Menu do utilizador" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Gerar registo de suporte" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "Acerca do GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Selecione um dispositivo" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Selecionar" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Nenhum dispositivo encontrado" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Lista de dispositivos" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Reportar" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Alguma coisa correu mal" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "O GSConnect encontrou um erro inesperado. Reporte o problema e inclua todas as informaƧƵes que possam ajudar." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Informação tĆ©cnica" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "Enviar para dispositivo móvel" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Dispositivos Móveis" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Ligar" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d ligado" msgstr[1] "%d ligado" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Desligar" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Editar" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Remover" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Desativado" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Introduza um novo atalho para alterar %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "O %s jĆ” estĆ” a ser usado" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Uma implementação completa do KDE Connect para o GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "Hugo Carvalho " #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "As mensagens de depuração estĆ£o a ser registadas. Tomar todas as medidas necessĆ”rias para reproduzir um problema e depois rever o registo." #: src/preferences/service.js:414 msgid "Review Log" msgstr "Registo de revisĆ£o" #: src/preferences/service.js:482 msgid "Laptop" msgstr "PortĆ”til" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Smartphone" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tablet" #: src/preferences/service.js:488 msgid "Television" msgstr "TelevisĆ£o" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Desemparelhado" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Desligado" #: src/preferences/service.js:518 msgid "Connected" msgstr "Ligado" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "ƀ espera de serviƧo…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "Clique para ajudar a solucionar problemas" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "Clique para mais informação" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Marcar nĆŗmero" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Partilhar ficheiro" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Listar dispositivos disponĆ­veis" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Listar todos os dispositivos" #: src/service/daemon.js:369 msgid "Target Device" msgstr "Para o dispositivo" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Corpo da mensagem" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Enviar notificação" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "Nome da aplicação de notificação" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Corpo da notificação" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "ƍcone de notificação" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "ID da notificação" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Foto" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Toque" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Partilhar ligação" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Partilhar texto" #: src/service/daemon.js:528 msgid "Show release version" msgstr "Mostrar versĆ£o de lanƧamento" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "NĆ£o disponĆ­vel" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Dispositivo Bluetooth em %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "%s impressĆ£o digital:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Pedido para emparelhar de %s" #: src/service/device.js:800 msgid "Reject" msgstr "Rejeitar" #: src/service/device.js:805 msgid "Accept" msgstr "Aceitar" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "A deteção foi desativada devido ao nĆŗmero de dispositivos nesta rede." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSL nĆ£o encontrado" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "Porta jĆ” em utilização" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "Trocar informaƧƵes de bateria" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: Bateria estĆ” carregada" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "Totalmente carregado" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "%s: Bateria atingiu o nĆ­vel de carga personalizada" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "%d%% Carregada" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: A bateria estĆ” fraca" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% restante" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Ɓrea de transferĆŖncia" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "Partilhar o conteĆŗdo da Ć”rea de transferĆŖncia" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "Enviar da Ć”rea de transferĆŖncia" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "Carregar da Ć”rea de transferĆŖncia" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "Aceder a contactos do dispositivo emparelhado" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Encontrar o meu telefone" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "Tocar no seu dispositivo emparelhado" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "Tapete de rato" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "Permite que o dispositivo emparelhado funcione como um rato e teclado remotos" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Teclado" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "Controlo remoto bidirecional de reprodução multimĆ©dia" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "Desconhecida" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "Partilhar notificaƧƵes com o dispositivo emparelhado" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Cancelar notificação" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Fechar notificação" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "Enviar notificação" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Ativar notificaƧƵes" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "Solicitar ao dispositivo emparelhado para tirar uma fotografia e transferi-la para este PC" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Falha ao transferir" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Falha ao enviar ā€œ%sā€ para %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "Envie e receba pings" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "Apresentação" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "Usar o dispositivo emparelhado como apresentador" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Executar comandos" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "Executar comandos no dispositivo emparelhado ou permitir que o dispositivo execute comandos predefinidos neste PC" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "Navegar pelo sistema de ficheiros do dispositivo emparelhado" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Montar" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Desmontar" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s relatou um erro" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Partilha" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "Partilhar ficheiros e URLs entre dispositivos" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s nĆ£o tem permissĆ£o para carregar ficheiros" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "A transferir ficheiro" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "A receber \"%s\" de %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "TransferĆŖncia bem sucedida" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "Recebeu \"%s\" de %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Abrir pasta" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Abrir ficheiro" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Falha ao receber ā€œ%sā€ de %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Texto partilhado por %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "A enviar \"%s\" para %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "Enviou \"%s\" para %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Enviar ficheiros para %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Abrir quando terminar" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Enviar um ligação para %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "Enviar e ler SMS do dispositivo emparelhado e ser notificado sobre o novo SMS" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Novo SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "Responder SMS" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "Partilhar SMS" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Volume do sistema" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "Ativar o dispositivo emparelhado para controlar o volume do sistema" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio nĆ£o encontrado" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "Ser notificado sobre as chamadas e ajustar o volume do sistema durante o toque/chamadas em curso" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Silenciar chamada" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Contacto desconhecido" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Chamada recebida" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Chamada em curso" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Fax" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Trabalho" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Telemóvel" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Casa" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Enviar para %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Agora mesmo" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Ontem惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minuto" msgstr[1] "%d minutos" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Mensagem para grupo" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "VocĆŖ: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "E %d outro contacto" msgstr[1] "E %d outros" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "Teclado remoto na %s nĆ£o estĆ” ativo" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (a estimar…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d∶%02d atĆ© ficar carregada)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d∶%02d restante)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Responder" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Partilhar ligaƧƵes com o GSConnect, diretamente para o navegador ou por SMS." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "ServiƧo indisponĆ­vel" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Abrir no navegador" gnome-shell-extension-gsconnect-50/po/pt_BR.po000066400000000000000000001035421421543444100215000ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-09 01:50\n" "Last-Translator: \n" "Language-Team: Portuguese, Brazilian\n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: pt-BR\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "Implementação do KDE Connect para o GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "Equipe GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnect Ć© uma implementação completa do KDE Connect, especialmente para GNOME Shell com integração com Nautilus, Chrome e Firefox. A equipe do KDE Connect possui aplicativos para Linux, BSD, Android, Sailfish, macOS e Windows." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "Com o GSConnect, vocĆŖ pode se conectar de forma segura a dispositivos móveis e outros desktops para:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Compartilhar arquivos, links e texto" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Enviar e receber mensagens" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Sincronizar o conteĆŗdo da Ć”rea de transferĆŖncia" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Sincronizar contatos" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Sincronizar notificaƧƵes" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Controlar reprodutores de mĆ­dia" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "Controlar o volume do sistema" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Executar comandos predefinidos" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "E mais…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect no GNOME Shell" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Conectar a…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Cancelar" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Conectar" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "EndereƧo IP" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "Nenhum contato" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Ajuda" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Digite um nĆŗmero de telefone ou nome" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Outros" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "Enviar SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Enviar" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Dispositivo estĆ” desconectado" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Enviar mensagem" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Escrever uma mensagem" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "Entrada de mensagem" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Digite uma mensagem e pressione Enter para enviar" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Mensagens" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Nova conversa" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Nenhuma conversa" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Nenhuma conversa selecionada" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Selecionar ou iniciar uma conversa" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Editar comando" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Salvar" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Nome" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Linha de comando" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Escolher um executĆ”vel" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Abrir" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Computador" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "CĆ¢mera" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Sincronização da Ć”rea de transferĆŖncia" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Reprodutores de mĆ­dia" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Mouse & teclado" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Controle de volume" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Arquivos" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Receber arquivos" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Salvar arquivos em" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Compartilhar" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Bateria do dispositivo" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Notificação de bateria fraca" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Notificação de bateria carregada" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Bateria do sistema" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Compartilhar estatĆ­sticas" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Bateria" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Comandos" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Adicionar comando" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Compartilhar notificaƧƵes" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Compartilhar quanto ativo" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Aplicativos" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "NotificaƧƵes" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Contatos" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Chamadas recebidas" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Volume" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Pausar mĆ­dia" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Chamadas em andamento" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Silenciar microfone" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Telefonia" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Atalhos de aƧƵes" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Redefinir tudo…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Atalhos" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Plugins" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Experimental" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Cache do dispositivo" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Limpar cache…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Suporte a SMS legado" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "Automontagem SFTP" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "AvanƧado" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Atalhos de teclado" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "ConfiguraƧƵes do dispositivo" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Parear" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "O dispositivo nĆ£o estĆ” pareado" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "VocĆŖ pode configurar este dispositivo antes de parear" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Informação de criptografia" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Esquecer" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Para o dispositivo" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Do dispositivo" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Nada" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Restaurar" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Baixo" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Silenciar" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Definir" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Pressione Esc para cancelar ou Backspace para redefinir o atalho de teclado." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Nome do dispositivo" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_Renomear" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Recarregar" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "ConfiguraƧƵes de dispositivos" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Menu do serviƧo" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "Menu do dispositivo" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Edita o nome do dispositivo" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Dispositivos" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Procurando dispositivos…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Complementos para navegadores" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Habilitar" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Este dispositivo estĆ” invisĆ­vel para dispositivos nĆ£o pareados" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Descoberta desativada" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Modo de exibição" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Painel" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "Menu do usuĆ”rio" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Gerar log de suporte" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "Sobre o GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Selecione um dispositivo" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Selecionar" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Nenhum dispositivo encontrado" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Lista de dispositivos" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Reportar" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Algo estĆ” errado" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "GSConnect encontrou um erro inesperado. Por favor, relate o problema e inclua todas as informaƧƵes que possam ajudar." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Detalhes tĆ©cnicos" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "Enviar para dispositivo móvel" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Dispositivos móveis" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Ativar" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d conectado" msgstr[1] "%d conectados" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Desativar" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Editar" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Remover" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Desativado" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Digite um novo atalho para mudar %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s jĆ” estĆ” em uso" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Uma implementação completa do KDE Connect para o GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "Ricardo Silva Veloso \n" "Rafael Fontenelle " #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "Mensagens de depuração estĆ£o sendo registradas. Utilize quaisquer etapas necessĆ”rias para reproduzir um problema e consulte o log." #: src/preferences/service.js:414 msgid "Review Log" msgstr "Consultar log" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Notebook" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Smartphone" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tablet" #: src/preferences/service.js:488 msgid "Television" msgstr "TelevisĆ£o" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "NĆ£o pareado" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Desconectado" #: src/preferences/service.js:518 msgid "Connected" msgstr "Conectado" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "Aguardando serviƧo…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "Clique para ajuda na solução de problemas" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "Clique para mais informação" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Ligar para nĆŗmero" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Compartilhar arquivo" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Listar dispositivos disponĆ­veis" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Listar todos dispositivos" #: src/service/daemon.js:369 msgid "Target Device" msgstr "Dispositivo alvo" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Corpo da mensagem" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Enviar notificação" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "Nome do aplicativo de notificação" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Corpo da notificação" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "ƍcone da notificação" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "ID da notificação" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Foto" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Tocar" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Compartilhar link" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Compartilhar texto" #: src/service/daemon.js:528 msgid "Show release version" msgstr "Mostrar versĆ£o de lanƧamento" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "IndisponĆ­vel" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Dispositivo Bluetooth em %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "ImpressĆ£o digital de %s:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Convite de pareamento de %s" #: src/service/device.js:800 msgid "Reject" msgstr "Rejeitar" #: src/service/device.js:805 msgid "Accept" msgstr "Aceitar" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "A descoberta foi desativada devido ao nĆŗmero de dispositivos nesta rede." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSL nĆ£o encontrado" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "A porta jĆ” estĆ” em uso" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: Bateria cheia" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "Carregado" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: bateria fraca" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% restante" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Ɓrea de transferĆŖncia" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "Enviar para Ć”rea de transferĆŖncia" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "Pegar da Ć”rea de transferĆŖncia" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Encontrar meu smartphone" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "Mousepad" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Teclado" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "Desconhecido" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Cancelar notificação" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Fechar notificação" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "Responder notificação" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Ativar notificação" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "TransferĆŖncia falhou" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Falha ao enviar ā€œ%sā€ para %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "Apresentação" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Executar comandos" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Montar" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Desmontar" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s relatou um erro" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Compartilhar" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s nĆ£o tem permissĆ£o para enviar arquivos" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "Transferindo arquivo" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "Recebendo ā€œ%sā€ de %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "TransferĆŖncia completa" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "Recebido ā€œ%sā€ de %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Abrir pasta" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Abrir arquivo" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Falha ao receber ā€œ%sā€ de %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Texto compartilhado por %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Enviando ā€œ%sā€ para %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "Enviado ā€œ%sā€ para %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Enviar arquivos para %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Abrir quando terminar" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Enviar um link para %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Novo SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "Responder SMS" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "Compartilhar SMS" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Volume do sistema" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio nĆ£o encontrado" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Silenciar chamada" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Contato desconhecido" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Recebendo chamada" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Chamada em andamento" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Fax" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Trabalho" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Celular" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Residencial" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Enviar para %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Agora" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Ontem惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minuto" msgstr[1] "%d minutos" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Mensagem de grupo" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "VocĆŖ: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "e %d outro contato" msgstr[1] "e %d outros" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "O teclado remoto em %s nĆ£o estĆ” ativo" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (Estimando…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d∶%02d atĆ© a carga completa)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d∶%02d restante)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Responder" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Compartilhe links com o GSConnect, diretamente no navegador ou por SMS." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "ServiƧo indisponĆ­vel" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Abrir no navegador" gnome-shell-extension-gsconnect-50/po/ru.po000066400000000000000000001160261421543444100211210ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-12-03 16:29\n" "Last-Translator: \n" "Language-Team: Russian\n" "Language: ru_RU\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: ru\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "Š ŠµŠ°Š»ŠøŠ·Š°Ń†ŠøŃ KDE Connect Š“Š»Ń GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "КоманГа GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnect - ŃŃ‚Š¾ ŠæŠ¾Š»Š½Š°Ń Ń€ŠµŠ°Š»ŠøŠ·Š°Ń†ŠøŃ KDE Connect Š“Š»Ń GNOME Shell с интеграцией в Nautilus, Chrome Šø Firefox. КоманГа KDE Connect также имеет ŠæŃ€ŠøŠ»Š¾Š¶ŠµŠ½ŠøŃ Š“Š»Ń Linux, BSD, Android, Sailfish, macOS Šø Windows." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "Š” ŠæŠ¾Š¼Š¾Ń‰ŃŒŃŽ GSConnect вы можете безопасно ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒŃŃ Šŗ Š¼Š¾Š±ŠøŠ»ŃŒŠ½Ń‹Š¼ ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š°Š¼ Šø Š“Ń€ŃƒŠ³ŠøŠ¼ ŠŗŠ¾Š¼ŠæŃŒŃŽŃ‚ŠµŃ€Š°Š¼:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "ŠŸŠ¾Š“ŠµŠ»ŠøŃ‚ŃŒŃŃ файлами, ссылками Šø текстом" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "ŠžŃ‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ Šø ŠæŠ¾Š»ŃƒŃ‡ŠøŃ‚ŃŒ ŃŠ¾Š¾Š±Ń‰ŠµŠ½ŠøŃ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Š”ŠøŠ½Ń…Ń€Š¾Š½ŠøŠ·ŠøŃ€Š¾Š²Š°Ń‚ŃŒ Š±ŃƒŃ„ŠµŃ€ обмена" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Š”ŠøŠ½Ń…Ń€Š¾Š½ŠøŠ·ŠøŃ€Š¾Š²Š°Ń‚ŃŒ контакты" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Š”ŠøŠ½Ń…Ń€Š¾Š½ŠøŠ·ŠøŃ€Š¾Š²Š°Ń‚ŃŒ ŃƒŠ²ŠµŠ“Š¾Š¼Š»ŠµŠ½ŠøŃ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Š£ŠæŃ€Š°Š²Š»ŃŃ‚ŃŒ ŠæŃ€Š¾ŠøŠ³Ń€Ń‹Š²Š°Ń‚ŠµŠ»ŃŠ¼Šø" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "Š£ŠæŃ€Š°Š²Š»ŃŃ‚ŃŒ системной Š³Ń€Š¾Š¼ŠŗŠ¾ŃŃ‚ŃŒŃŽ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Š’Ń‹ŠæŠ¾Š»Š½ŃŃ‚ŃŒ заГанные команГы" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "И Š“Ń€ŃƒŠ³Š¾Šµā€¦" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect в GNOME Shell" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "ŠŸŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒŃŃ к…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "ŠžŃ‚Š¼ŠµŠ½Š°" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "ŠŸŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒŃŃ" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP аГрес" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "ŠŠµŃ‚ контактов" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "ŠŸŠ¾Š¼Š¾Ń‰ŃŒ" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "ŠŠ°Š±ŠµŃ€ŠøŃ‚Šµ номер или ŠøŠ¼Ń" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Š”Ń€ŃƒŠ³ŠøŠµ" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "ŠžŃ‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ ДМД" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "ŠžŃ‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Устройство Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "ŠžŃ‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ сообщение" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "ŠŠ°Š±Ń€Š°Ń‚ŃŒ сообщение" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "Дообщение" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "ВвеГите сообщение Šø нажмите Enter Š“Š»Ń отправки" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Дообщение" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "ŠŠ¾Š²Ń‹Š¹ Гиалог" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "ŠŠµŃ‚ Гиалогов" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "ŠŠµ выбран Гиалог" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Выберите или начните Гиалог" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Š˜Š·Š¼ŠµŠ½ŠøŃ‚ŃŒ команГу" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Š”Š¾Ń…Ń€Š°Š½ŠøŃ‚ŃŒ" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Š˜Š¼Ń" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "ŠšŠ¾Š¼Š°Š½Š“Š½Š°Ń строка" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Выберите ŠøŃŠæŠ¾Š»Š½ŃŠµŠ¼Ń‹Š¹ файл" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "ŠžŃ‚ŠŗŃ€Ń‹Ń‚ŃŒ" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "ŠšŠ¾Š¼ŠæŃŒŃŽŃ‚ŠµŃ€" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "ŠšŠ°Š¼ŠµŃ€Š°" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Š”ŠøŠ½Ń…Ń€Š¾Š½ŠøŠ·Š°Ń†ŠøŃ Š±ŃƒŃ„ŠµŃ€Š° обмена" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "ŠŸŃ€Š¾ŠøŠ³Ń€Ń‹Š²Š°Ń‚ŠµŠ»Šø" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "ŠœŃ‹ŃˆŃŒ Šø ŠŗŠ»Š°Š²ŠøŠ°Ń‚ŃƒŃ€Š°" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Управление Š³Ń€Š¾Š¼ŠŗŠ¾ŃŃ‚ŃŒŃŽ" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Файлы" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "ŠŸŃ€ŠøŠ½ŃŃ‚ŃŒ файлы" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Š”Š¾Ń…Ń€Š°Š½ŠøŃ‚ŃŒ файлы в" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "ŠžŠ±Ń‰ŠøŠ¹ Š“Š¾ŃŃ‚ŃƒŠæ Šø обмен" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Š‘Š°Ń‚Š°Ń€ŠµŃ ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š°" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "УвеГомление о низком ŃƒŃ€Š¾Š²Š½Šµ Š·Š°Ń€ŃŠ“Š°" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "УвеГомление о Š·Š°Ń€ŃŠ“ке Го ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»ŃŒŃŠŗŠ¾Š³Š¾ ŃƒŃ€Š¾Š²Š½Ń" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "УвеГомление о полной Š·Š°Ń€ŃŠ“ке" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Š”ŠøŃŃ‚ŠµŠ¼Š½Š°Ń Š±Š°Ń‚Š°Ń€ŠµŃ" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "ŠŸŠ¾Š“ŠµŠ»ŠøŃ‚ŃŒŃŃ статистикой" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Š‘Š°Ń‚Š°Ń€ŠµŃ" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "ŠšŠ¾Š¼Š°Š½Š“Ń‹" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Š”Š¾Š±Š°Š²ŠøŃ‚ŃŒ команГу" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "ŠžŃ‚ŠæŃ€Š°Š²Š»ŃŃ‚ŃŒ ŃƒŠ²ŠµŠ“Š¾Š¼Š»ŠµŠ½ŠøŃ" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "ŠŸŠ¾Š“ŠµŠ»ŠøŃ‚ŃŒŃŃ при Š“Š¾ŃŃ‚ŃƒŠæŠ½Š¾ŃŃ‚Šø" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "ŠŸŃ€ŠøŠ»Š¾Š¶ŠµŠ½ŠøŃ" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Š£Š²ŠµŠ“Š¾Š¼Š»ŠµŠ½ŠøŃ" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "ŠšŠ¾Š½Ń‚Š°ŠŗŃ‚Ń‹" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "ŠŸŃ€Šø Š²Ń…Š¾Š“ŃŃ‰ŠµŠ¼ вызове" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Š“Ń€Š¾Š¼ŠŗŠ¾ŃŃ‚ŃŒ" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "ŠŸŃ€ŠøŠ¾ŃŃ‚Š°Š½Š¾Š²ŠøŃ‚ŃŒ плеер" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "ŠŸŃ€Šø ŠøŃŃ…Š¾Š“ŃŃ‰ŠµŠ¼ вызове" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Š’Ń‹ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒ микрофон" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Š¢ŠµŠ»ŠµŃ„Š¾Š½ŠøŃ" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "ŠšŠ¾Š¼Š±ŠøŠ½Š°Ń†ŠøŠø клавиш" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Š”Š±Ń€Š¾ŃŠøŃ‚ŃŒ все…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "ŠšŠ¾Š¼Š±ŠøŠ½Š°Ń†ŠøŠø клавиш" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "ŠŸŠ»Š°Š³ŠøŠ½Ń‹" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Š­ŠŗŃŠæŠµŃ€ŠøŠ¼ŠµŠ½Ń‚Š°Š»ŃŒŠ½Š¾Šµ" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Кеш ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š°" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "ŠžŃ‡ŠøŃŃ‚ŠøŃ‚ŃŒ ŠŗŠµŃˆā€¦" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Š£ŃŃ‚Š°Ń€ŠµŠ²ŃˆŠ°Ń поГГержка SMS" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "Автомонтирование SFTP" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "Š”Š¾ŠæŠ¾Š»Š½ŠøŃ‚ŠµŠ»ŃŒŠ½Ń‹Šµ" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "ŠšŠ¾Š¼Š±ŠøŠ½Š°Ń†ŠøŠø клавиш" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "ŠŠ°ŃŃ‚Ń€Š¾Š¹ŠŗŠø ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š°" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Š”Š¾ŠæŃ€ŃŠ¶ŠµŠ½ŠøŠµ" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "Устройство не ŃŠ¾ŠæŃ€ŃŠ¶ŠµŠ½Š¾" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Š’Ń‹ можете Š½Š°ŃŃ‚Ń€Š¾ŠøŃ‚ŃŒ ŃŃ‚Š¾ ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾ переГ ŃŠ¾ŠæŃ€ŃŠ¶ŠµŠ½ŠøŠµŠ¼" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Š˜Š½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ о ŃˆŠøŃ„Ń€Š¾Š²Š°Š½ŠøŠø" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Š—Š°Š±Ń‹Ń‚ŃŒ" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "ŠŠ° ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Š” ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š°" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "ŠŠøŃ‡ŠµŠ³Š¾" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Š’ŠµŃ€Š½ŃƒŃ‚ŃŒ" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Тише" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Š’Ń‹ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒ" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Выбор" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "ŠŠ°Š¶Š¼ŠøŃ‚Šµ Esc Š“Š»Ń отмены или Backspace чтобы ŃŠ±Ń€Š¾ŃŠøŃ‚ŃŒ ŠŗŠ¾Š¼Š±ŠøŠ½Š°Ń†ŠøŃŽ." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "ŠŠ°Š·Š²Š°Š½ŠøŠµ ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š°" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_ŠŸŠµŃ€ŠµŠøŠ¼ŠµŠ½Š¾Š²Š°Ń‚ŃŒ" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "ŠžŠ±Š½Š¾Š²ŠøŃ‚ŃŒ" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "ŠŠ°ŃŃ‚Ń€Š¾Š¹ŠŗŠø" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Дервисное Š¼ŠµŠ½ŃŽ" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "ŠœŠµŠ½ŃŽ ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š°" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Š ŠµŠ“Š°ŠŗŃ‚ŠøŃ€Š¾Š²Š°Ń‚ŃŒ название ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š°" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Устройства" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Поиск ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²ā€¦" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Š Š°ŃŃˆŠøŃ€ŠµŠ½ŠøŃ Š±Ń€Š°ŃƒŠ·ŠµŃ€Š°" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Š’ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒ" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Это ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾ ŃŠ²Š»ŃŠµŃ‚ŃŃ невиГимым Š“Š»Ń неспаренных ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "ŠžŠ±Š½Š°Ń€ŃƒŠ¶ŠµŠ½ŠøŠµ Š²Ń‹ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Режим Š¾Ń‚Š¾Š±Ń€Š°Š¶ŠµŠ½ŠøŃ" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Панель" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "ŠœŠµŠ½ŃŽ ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Š”Š³ŠµŠ½ŠµŃ€ŠøŃ€Š¾Š²Š°Ń‚ŃŒ Š¶ŃƒŃ€Š½Š°Š»" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "Šž GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Выберите ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Выбор" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Устройства не найГены" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Дписок ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "ŠžŃ‚Š·Ń‹Š²" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Что-то пошло не так" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "Возникла Š½ŠµŠæŃ€ŠµŠ“Š²ŠøŠ“ŠµŠ½Š½Š°Ń ошибка. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, сообщите о проблеме, по возможности ŠæŃ€ŠµŠ“Š¾ŃŃ‚Š°Š²ŃŒŃ‚Šµ Š“Š¾ŠæŠ¾Š»Š½ŠøŃ‚ŠµŠ»ŃŒŠ½ŃƒŃŽ ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃŽ." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Š¢ŠµŃ…Š½ŠøŃ‡ŠµŃŠŗŠ°Ń ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "ŠžŃ‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ на ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Устройства" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Š’ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒ" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½" msgstr[1] "%d ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾" msgstr[2] "%d ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾" msgstr[3] "%d ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Š’Ń‹ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒ" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Š˜Š·Š¼ŠµŠ½ŠøŃ‚ŃŒ" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Š£Š“Š°Š»ŠøŃ‚ŃŒ" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "ŠžŃ‚ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "ВвеГите Š½Š¾Š²ŃƒŃŽ ŠŗŠ¾Š¼Š±ŠøŠ½Š°Ń†ŠøŃŽ клавиш Š“Š»Ń %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s уже ŠøŃŠæŠ¾Š»ŃŒŠ·ŃƒŠµŃ‚ся" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "ŠŸŠ¾Š»Š½Š°Ń Ń€ŠµŠ°Š»ŠøŠ·Š°Ń†ŠøŃ KDE Connect Š“Š»Ń GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "'Losted' " #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "ŠžŃ‚Š»Š°Š“Š¾Ń‡Š½Ń‹Šµ ŃŠ¾Š¾Š±Ń‰ŠµŠ½ŠøŃ Š±ŃƒŠ“ŃƒŃ‚ записаны. ŠŸŃ€Š¾ŠøŠ·Š²ŠµŠ“ŠøŃ‚Šµ Š“ŠµŠ¹ŃŃ‚Š²ŠøŃ при которых ŠæŃ€Š¾ŠøŠ·Š¾ŃˆŠ»Š° проблема, затем посмотрите Š¶ŃƒŃ€Š½Š°Š»." #: src/preferences/service.js:414 msgid "Review Log" msgstr "ŠŸŠ¾ŃŠ¼Š¾Ń‚Ń€ŠµŃ‚ŃŒ Š¶ŃƒŃ€Š½Š°Š»" #: src/preferences/service.js:482 msgid "Laptop" msgstr "ŠŠ¾ŃƒŃ‚Š±ŃƒŠŗ" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Дмартфон" #: src/preferences/service.js:486 msgid "Tablet" msgstr "ŠŸŠ»Š°Š½ŃˆŠµŃ‚" #: src/preferences/service.js:488 msgid "Television" msgstr "ТелевиГение" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "ŠŠµ ŃŠ¾ŠæŃ€ŃŠ¶ŠµŠ½" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "ŠžŃ‚ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾" #: src/preferences/service.js:518 msgid "Connected" msgstr "ŠŸŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "ŠžŠ¶ŠøŠ“Š°Š½ŠøŠµ ŃŠ»ŃƒŠ¶Š±Ń‹ā€¦" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "ŠŠ°Š¶Š¼ŠøŃ‚Šµ Š“Š»Ń Ń€ŠµŃˆŠµŠ½ŠøŃ проблем" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "ŠŠ°Š¶Š¼ŠøŃ‚Šµ Š“Š»Ń ŠæŠ¾Š»ŃƒŃ‡ŠµŠ½ŠøŃ информации" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Š’Ń‹Š·Š²Š°Ń‚ŃŒ номер" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "ŠŸŠ¾Š“ŠµŠ»ŠøŃ‚ŃŒŃŃ файлом" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Дписок Š“Š¾ŃŃ‚ŃƒŠæŠ½Ń‹Ń… ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Дписок всех ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²" #: src/service/daemon.js:369 msgid "Target Device" msgstr "Целевое ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Текст ŃŠ¾Š¾Š±Ń‰ŠµŠ½ŠøŃ" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "ŠžŃ‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "Š˜Š¼Ń ŠæŃ€ŠøŠ»Š¾Š¶ŠµŠ½ŠøŃ" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Текст ŃƒŠ²ŠµŠ“Š¾Š¼Š»ŠµŠ½ŠøŃ" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Иконка ŃƒŠ²ŠµŠ“Š¾Š¼Š»ŠµŠ½ŠøŃ" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "ID ŃƒŠ²ŠµŠ“Š¾Š¼Š»ŠµŠ½ŠøŃ" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Фото" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Пинг" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "ŠŠ°Š¹Ń‚Šø" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "ŠŸŠ¾Š“ŠµŠ»ŠøŃ‚ŃŒŃŃ ссылкой" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "ŠŸŠ¾Š“ŠµŠ»ŠøŃ‚ŃŒŃŃ текстом" #: src/service/daemon.js:528 msgid "Show release version" msgstr "ŠŸŠ¾ŠŗŠ°Š·Š°Ń‚ŃŒ Š²ŠµŃ€ŃŠøŃŽ программы" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "ŠŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½Š¾" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Bluetooth ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾ на %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "ŠžŃ‚ŠæŠµŃ‡Š°Ń‚Š¾Šŗ %s:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Запрос ŃŠ¾ŠæŃ€ŃŠ¶ŠµŠ½ŠøŃ от %s" #: src/service/device.js:800 msgid "Reject" msgstr "ŠžŃ‚ŠŗŠ»Š¾Š½ŠøŃ‚ŃŒ" #: src/service/device.js:805 msgid "Accept" msgstr "ŠŸŃ€ŠøŠ½ŃŃ‚ŃŒ" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "ŠžŠ±Š½Š°Ń€ŃƒŠ¶ŠµŠ½ŠøŠµ было Š²Ń‹ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾ ŠøŠ·-за количества ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š² в ŃŃ‚Š¾Š¹ сети." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSL не найГен" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "ŠŸŠ¾Ń€Ń‚ уже ŠøŃŠæŠ¾Š»ŃŒŠ·ŃƒŠµŃ‚ся" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "ŠžŠæŠ¾Š²ŠµŃ‰Š°Ń‚ŃŒ о ŃŠ¾ŃŃ‚Š¾ŃŠ½ŠøŠø батареи" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: ŠŠŗŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚Š¾Ń€ Š·Š°Ń€ŃŠ¶ŠµŠ½" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "ŠŸŠ¾Š»Š½Š¾ŃŃ‚ŃŒŃŽ Š·Š°Ń€ŃŠ¶ŠµŠ½Š¾" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "%s: Š£Ń€Š¾Š²ŠµŠ½ŃŒ Š·Š°Ń€ŃŠ“Š° батареи Гостиг ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»ŃŒŃŠŗŠ¾Š¹ отметки" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "%d%% Š·Š°Ń€ŃŠ¶ŠµŠ½Š¾" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: ŠŠŗŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚Š¾Ń€ Ń€Š°Š·Ń€ŃŠ¶ŠµŠ½" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% Š¾ŃŃ‚Š°Š»Š¾ŃŃŒ" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Š‘ŃƒŃ„ŠµŃ€ обмена" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "ŠŸŠ¾Š“ŠµŠ»ŠøŃ‚ŃŒŃŃ соГержимым Š±ŃƒŃ„ера обмена" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "ŠžŃ‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ Š‘ŃƒŃ„ŠµŃ€ обмена" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "Š—Š°ŠæŃ€Š¾ŃŠøŃ‚ŃŒ Š‘ŃƒŃ„ŠµŃ€ обмена" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "ŠŸŠ¾Š»ŃƒŃ‡ŠøŃ‚ŃŒ Š“Š¾ŃŃ‚ŃƒŠæ Šŗ контактам ŃŠ¾ŠæŃ€ŃŠ¶Ń‘Š½Š½Š¾Š³Š¾ ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š°" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "ŠŠ°Š¹Ń‚Šø мой смартфон" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "Š”Š“ŠµŠ»Š°Ń‚ŃŒ гуГок вашим ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾Š¼" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "ТачпаГ" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "ŠŸŠ¾Š·Š²Š¾Š»ŃŠµŃ‚ ŃŠ¾ŠæŃ€ŃŠ¶Ń‘Š½Š½Š¾Š¼Ńƒ ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Ńƒ ŃƒŠ“Š°Š»Ń‘Š½Š½Š¾ Ń€Š°Š±Š¾Ń‚Š°Ń‚ŃŒ Š¼Ń‹ŃˆŃŒŃŽ Šø ŠŗŠ»Š°Š²ŠøŠ°Ń‚ŃƒŃ€Š¾Š¹" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "ŠšŠ»Š°Š²ŠøŠ°Ń‚ŃƒŃ€Š°" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "Š”Š²ŃƒŠ½Š°ŠæŃ€Š°Š²Š»Ń‘Š½Š½Š¾Šµ ŃƒŠæŃ€Š°Š²Š»ŠµŠ½ŠøŠµ воспроизвеГением" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š¾" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "ŠŸŠ¾Š“ŠµŠ»ŠøŃ‚ŃŒŃŃ ŃƒŠ²ŠµŠ“Š¾Š¼Š»ŠµŠ½ŠøŃŠ¼Šø с ŃŠ¾ŠæŃ€ŃŠ¶Ń‘Š½Š½Ń‹Š¼ ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾Š¼" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "ŠžŃ‚Š¼ŠµŠ½ŠøŃ‚ŃŒ увеГомление" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Š—Š°ŠŗŃ€Ń‹Ń‚ŃŒ увеГомление" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "ŠžŃ‚Š²ŠµŃ‚ŠøŃ‚ŃŒ" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "ŠŠŗŃ‚ŠøŠ²ŠøŃ€Š¾Š²Š°Ń‚ŃŒ увеГомление" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "Š—Š°ŠæŃ€Š¾ŃŠøŃ‚ŃŒ ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾ ŃŠ“ŠµŠ»Š°Ń‚ŃŒ снимок Šø Š¾Ń‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ его на ŃŃ‚Š¾Ń‚ ПК" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "ŠŸŠµŃ€ŠµŠ“Š°Ń‡Š° не уГалась" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "ŠŠµ уГалось Š¾Ń‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ Ā«%sĀ» на %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "ŠžŃ‚ŠæŃ€Š°Š²ŠŗŠ° Šø ŠæŠ¾Š»ŃƒŃ‡ŠµŠ½ŠøŠµ пингов" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Пинг: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "ŠŸŃ€ŠµŠ·ŠµŠ½Ń‚Š°Ń†ŠøŃ" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "Š˜ŃŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŃŒ ŃŠ¾ŠæŃ€ŃŠ¶Ń‘Š½Š½Š¾Šµ ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾ Š“Š»Ń презентации" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "ŠžŃ‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ КоманГу" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "Š’Ń‹ŠæŠ¾Š»Š½ŠøŃ‚ŃŒ команГы на ŃŠ¾ŠæŃ€ŃŠ¶Ń‘Š½Š½Š¾Š¼ ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Šµ или ŠæŠ¾Š·Š²Š¾Š»ŠøŃ‚ŃŒ ему Š·Š°ŠæŃƒŃŃ‚ŠøŃ‚ŃŒ опреГелённые команГы на ŃŃ‚Š¾Š¼ ПК" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "ŠŸŃ€Š¾ŃŠ¼Š¾Ń‚Ń€ŠµŃ‚ŃŒ Ń„Š°Š¹Š»Š¾Š²ŃƒŃŽ ŃŠøŃŃ‚ŠµŠ¼Ńƒ ŃŠ¾ŠæŃ€ŃŠ¶Ń‘Š½Š½Š¾Š³Š¾ ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š°" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "ŠŸŃ€ŠøŠ¼Š¾Š½Ń‚ŠøŃ€Š¾Š²Š°Ń‚ŃŒ" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Š Š°Š·Š¼Š¾Š½Ń‚ŠøŃ€Š¾Š²Š°Ń‚ŃŒ" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s сообщил об ошибке" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "ŠŸŠ¾Š“ŠµŠ»ŠøŃ‚ŃŒŃŃ" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "Š”ŠµŠ»ŠøŃ‚ŃŒŃŃ файлами Šø ссылками межГу ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š°Š¼Šø" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s не Ń€Š°Š·Ń€ŠµŃˆŠµŠ½Š¾ Š·Š°Š³Ń€ŃƒŠ¶Š°Ń‚ŃŒ файлы" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "ŠŸŠµŃ€ŠµŠ“Š°Ń‡Š° файла" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "ŠŸŠ¾Š»ŃƒŃ‡ŠµŠ½ŠøŠµ Ā«%sĀ» от %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "ŠŸŠµŃ€ŠµŠ“Š°Ń‡Š° Š·Š°Š²ŠµŃ€ŃˆŠµŠ½Š°" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "ŠŸŠ¾Š»ŃƒŃ‡ŠµŠ½ Ā«%sĀ» от %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "ŠžŃ‚ŠŗŃ€Ń‹Ń‚ŃŒ папку" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "ŠžŃ‚ŠŗŃ€Ń‹Ń‚ŃŒ файл" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "ŠŠµ уГалось ŠæŠ¾Š»ŃƒŃ‡ŠøŃ‚ŃŒĀ«%sĀ» от %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Текст ŠæŠ¾Š»ŃƒŃ‡ŠµŠ½ с %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "ŠžŃ‚ŠæŃ€Š°Š²ŠŗŠ° Ā«%sĀ» на %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "ŠžŃ‚ŠæŃ€Š°Š²Š»ŠµŠ½Š¾ Ā«%sĀ» на %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "ŠžŃ‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ файлы на %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "ŠžŃ‚ŠŗŃ€Ń‹Ń‚ŃŒ при Š·Š°Š²ŠµŃ€ŃˆŠµŠ½ŠøŠø" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "ŠžŃ‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ ŃŃŃ‹Š»ŠŗŃƒ на %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "ŠžŃ‚ŠæŃ€Š°Š²ŠŗŠ° Šø чтение SMS с ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š° Šø ŠæŠ¾Š»ŃƒŃ‡ŠµŠ½ŠøŠµ увеГомлений о новых SMS" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "ŠŠ¾Š²Š¾Šµ сообщение (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "ŠžŃ‚Š²ŠµŃ‚ŠøŃ‚ŃŒ на SMS" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "ŠžŃ‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ SMS" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Š“Ń€Š¾Š¼ŠŗŠ¾ŃŃ‚ŃŒ" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "Š Š°Š·Ń€ŠµŃˆŠøŃ‚ŃŒ ŃŠ¾ŠæŃ€ŃŠ¶Ń‘Š½Š½Š¾Š¼Ńƒ ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Ńƒ ŃƒŠæŃ€Š°Š²Š»ŃŃ‚ŃŒ Š³Ń€Š¾Š¼ŠŗŠ¾ŃŃ‚ŃŒŃŽ системы" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio не найГен" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "Š£Š²ŠµŠ“Š¾Š¼Š»ŃŃ‚ŃŒ о звонках Šø Ń€ŠµŠ³ŃƒŠ»ŠøŃ€Š¾Š²Š°Ń‚ŃŒ Š³Ń€Š¾Š¼ŠŗŠ¾ŃŃ‚ŃŒ системы во Š²Ń€ŠµŠ¼Ń звонков" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "ŠžŃ‚ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒ звонок" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Ń‹Š¹ контакт" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Š’Ń…Š¾Š“ŃŃ‰ŠøŠ¹ звонок" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Š˜ŃŃ…Š¾Š“ŃŃ‰ŠøŠ¹ звонок" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Факс" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Рабочий" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "ŠœŠ¾Š±ŠøŠ»ŃŒŠ½Ń‹Š¹" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Š”Š¾Š¼Š°ŃˆŠ½ŠøŠ¹" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "ŠžŃ‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ на %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Только что" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Š’Ń‡ŠµŃ€Š°ćƒ»%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d Š¼ŠøŠ½ŃƒŃ‚Š°" msgstr[1] "%d Š¼ŠøŠ½ŃƒŃ‚Ń‹" msgstr[2] "%d Š¼ŠøŠ½ŃƒŃ‚" msgstr[3] "%d Š¼ŠøŠ½ŃƒŃ‚Ń‹" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Š“Ń€ŃƒŠæŠæŠ¾Š²Š¾Šµ сообщение" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Š’Ń‹: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "И %d Š“Ń€ŃƒŠ³Š¾Š¹ контакт" msgstr[1] "И %d Š“Ń€ŃƒŠ³ŠøŃ…" msgstr[2] "И %d Š“Ń€ŃƒŠ³ŠøŃ…" msgstr[3] "И %d Š“Ń€ŃƒŠ³ŠøŃ…" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "Š£Š“Š°Š»ŠµŠ½Š½Š°Ń ŠŗŠ»Š°Š²ŠøŠ°Ń‚ŃƒŃ€Š° на %s не активна" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (ŠžŃŃ‚Š°Š»Š¾ŃŃŒā€¦)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d∶%02d До полного Š·Š°Ń€ŃŠ“а)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d∶%02d ŠžŃŃ‚Š°Š»Š¾ŃŃŒ)" #: src/shell/notification.js:54 msgid "Reply" msgstr "ŠžŃ‚Š²ŠµŃ‚ŠøŃ‚ŃŒ" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "ŠžŃ‚ŠæŃ€Š°Š²Š»ŃŃ‚ŃŒ ссылки в веб Š±Ń€Š°ŃƒŠ·ŠµŃ€ или по SMS." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Дервис Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠµŠ½" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "ŠžŃ‚ŠŗŃ€Ń‹Ń‚ŃŒ в Š±Ń€Š°ŃƒŠ·ŠµŃ€Šµ" gnome-shell-extension-gsconnect-50/po/sk.po000066400000000000000000001100501421543444100210770ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the org.gnome.Shell.Extensions.GSConnect package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: org.gnome.Shell.Extensions.GSConnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2019-09-29 16:23+0200\n" "Last-Translator: Jose Riha \n" "Language-Team: \n" "Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n>=2 && n<=4 ? 1 : 2);\n" "X-Generator: Poedit 2.2.1\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 #, fuzzy msgid "KDE Connect implementation for GNOME" msgstr "KompletnĆ” implementĆ”cia aplikĆ”cie KDE Connect pre prostredie GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 #, fuzzy msgid "GSConnect Team" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "" "GSConnect is a complete implementation of KDE Connect especially for GNOME " "Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team " "has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "" "With GSConnect you can securely connect to mobile devices and other desktops " "to:" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 #, fuzzy msgid "Sync contacts" msgstr "Žiadne kontakty" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 #, fuzzy msgid "Sync notifications" msgstr "OdoslaÅ„ oznĆ”menie" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 #, fuzzy msgid "Control media players" msgstr "MultimediĆ”lne prehrĆ”vače" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 #, fuzzy msgid "Control system volume" msgstr "SystĆ©movĆ” hlasitosÅ„" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "PripojiÅ„ k…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "ZruÅ”iÅ„" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "PripojiÅ„" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP adresa" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "Žiadne kontakty" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "PomocnĆ­k" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Zadajte telefónne čƭslo alebo meno" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "InĆ©" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "OdoslaÅ„ SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "OdoslaÅ„" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Zariadenie je odpojenĆ©" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "OdoslaÅ„ sprĆ”vu" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Zadajte sprĆ”vu" #: data/ui/messaging-conversation.ui:92 #, fuzzy msgid "Message Entry" msgstr "Telo sprĆ”vy" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "PĆ­sanie sprĆ”v" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "NovĆ” konverzĆ”cia" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Žiadna konverzĆ”cia" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Nie je vybranĆ” konverzĆ”cia" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Vyberte alebo začnite novĆŗ konverzĆ”ciu" #: data/ui/preferences-command-editor.ui:7 #, fuzzy msgid "Edit Command" msgstr "PrĆ­kazy" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "UložiÅ„" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "NĆ”zov" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "PrĆ­kazový riadok" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "ZvolĆ­ spustiteľný sĆŗbor" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "OtvoriÅ„" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Stolný počƭtač" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "FotoaparĆ”t" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "SynchronizĆ”cia schrĆ”nky" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "MultimediĆ”lne prehrĆ”vače" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "MyÅ” a klĆ”vesnica" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "OvlĆ”danie hlasitosti" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "SĆŗbory" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "PrijaÅ„ sĆŗbory" #: data/ui/preferences-device-panel.ui:497 #, fuzzy msgid "Save files to" msgstr "Odoslanie sĆŗborov do zariadenia %s" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Zdieľanie" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Stav batĆ©rie" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Upozornenie na slabĆŗ batĆ©riu" #: data/ui/preferences-device-panel.ui:699 #, fuzzy msgid "Charged Up to Custom Level Notification" msgstr "OznĆ”menie o plnom nabitĆ­" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "OznĆ”menie o plnom nabitĆ­" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "SystĆ©movĆ” batĆ©ria" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "ZdieľaÅ„ Å”tatistiky" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "BatĆ©ria" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "PrĆ­kazy" #: data/ui/preferences-device-panel.ui:1025 #, fuzzy msgid "Add Command" msgstr "PrĆ­kazy" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "ZdieľaÅ„ oznĆ”menia" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "AplikĆ”cie" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "OznĆ”menia" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Kontakty" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "PrichĆ”dzajĆŗce hovory" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "HlasitosÅ„" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "PozastaviÅ„ mĆ©diĆ”" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "OdchĆ”dzajĆŗce hovory" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "StĆ­Å”iÅ„ mikrofón" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Telefonovanie" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Skratky akciĆ­" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "ObnoviÅ„ vÅ”etko…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Skratky" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "ZĆ”suvnĆ© moduly" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "ExperimentĆ”lne" #: data/ui/preferences-device-panel.ui:2000 #, fuzzy msgid "Device Cache" msgstr "Meno zariadenia" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Podpora SMS (zastaranĆ©)" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "PokročilĆ©" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "KlĆ”vesovĆ© skratky" #: data/ui/preferences-device-panel.ui:2513 #, fuzzy msgid "Device Settings" msgstr "MobilnĆ© nastavenia" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "SpĆ”rovaÅ„" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "SpĆ”rovanie so zariadenĆ­m bolo zruÅ”enĆ©" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Pred spĆ”rovanĆ­m mÓžete toto zariadenie nastaviÅ„" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "InformĆ”cie o Å”ifrovanĆ­" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "ZruÅ”iÅ„ pĆ”rovanie" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Do zariadenia" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Zo zariadenia" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Bez zmeny" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "TichÅ”ie" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "StĆ­Å”iÅ„" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "NastaviÅ„" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "" "Stlačte klĆ”vesu Esc na zruÅ”enie alebo klĆ”vesu Backspace na obnovenie " "klĆ”vesovej skratky." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Meno zariadenia" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_PremenovaÅ„" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "ObnoviÅ„" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "MobilnĆ© nastavenia" #: data/ui/preferences-window.ui:159 #, fuzzy msgid "Service Menu" msgstr "Služba" #: data/ui/preferences-window.ui:182 #, fuzzy msgid "Device Menu" msgstr "Meno zariadenia" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "UpraviÅ„ meno zariadenia" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Zariadenia" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "HľadajĆŗ sa zariadenia…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Doplnky prehliadačov" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "PovoliÅ„" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Toto zariadenie nie je viditeľnĆ© pre nespĆ”rovanĆ© zariadenia" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Objavenie zakĆ”zanĆ©" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Režim zobrazenia" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Panel" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "UžívateľskĆ” ponuka" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "VytvoriÅ„ zĆ”znam pre technickĆŗ podporu" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "O programe GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Výber zariadenia" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "VybraÅ„" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "NenaÅ”lo sa žiadne zariadenie" #: data/ui/service-device-chooser.ui:111 #, fuzzy msgid "Device List" msgstr "Zariadenia" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "OhlĆ”siÅ„" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "" #: data/ui/service-error-dialog.ui:84 msgid "" "GSConnect encountered an unexpected error. Please report the problem and " "include any information that may help." msgstr "" #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "OdoslaÅ„ do mobilnĆ©ho zariadenia" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "MobilnĆ© zariadenia" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "ZapnĆŗÅ„" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d pripojenĆ©" msgstr[1] "%d pripojenĆ©" msgstr[2] "%d pripojených" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "VypnĆŗÅ„" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "UpraviÅ„" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "OdstrĆ”niÅ„" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "ZakĆ”zanĆ©" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Zadajte novĆŗ skratku pre zmenu akcie %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "KlĆ”vesovĆ” skratka %s sa už používa" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "KompletnĆ” implementĆ”cia aplikĆ”cie KDE Connect pre prostredie GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "DuÅ”an Kazik , Jose Riha " #: src/preferences/service.js:411 msgid "" "Debug messages are being logged. Take any steps necessary to reproduce a " "problem then review the log." msgstr "" "SprĆ”vy ladenia sĆŗ zaznamenĆ”vanĆ©. PokĆŗste sa opƤtovne vyvolaÅ„ problĆ©m a " "pozrite sa na sĆŗbor so zĆ”znamom." #: src/preferences/service.js:414 msgid "Review Log" msgstr "PrezrieÅ„ sĆŗbor so zĆ”znamom" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Notebook" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Smartphone" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tablet" #: src/preferences/service.js:488 msgid "Television" msgstr "TelevĆ­zia" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "SpĆ”rovanie zruÅ”enĆ©" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "OdpojenĆ©" #: src/preferences/service.js:518 msgid "Connected" msgstr "PripojenĆ©" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "ČakĆ” sa na službu…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "KliknutĆ­m zĆ­skate pomoc pri rieÅ”enĆ­ problĆ©mu" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "KliknutĆ­m zĆ­skate viac informĆ”ciĆ­" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "VytočiÅ„ čƭslo" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "ZdieľaÅ„ sĆŗbor" #: src/service/daemon.js:351 msgid "List available devices" msgstr "ZobraziÅ„ dostupnĆ© zariadenia" #: src/service/daemon.js:360 msgid "List all devices" msgstr "ZobraziÅ„ vÅ”etky zariadenia" #: src/service/daemon.js:369 msgid "Target Device" msgstr "CieľovĆ© zariadenie" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Telo sprĆ”vy" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "OdoslaÅ„ oznĆ”menie" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "Meno aplikĆ”cie v oznĆ”menĆ­" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Telo oznĆ”menia" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Ikona oznĆ”menia" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "ID oznĆ”menia" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Fotografia" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "PrezvoniÅ„" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "ZdieľaÅ„ odkaz" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "ZdieľaÅ„ text" #: src/service/daemon.js:528 msgid "Show release version" msgstr "ZobraziÅ„ verziu vydania" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Nedostupný" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Zariadenie Bluetooth na adrese %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "Odtlačok zariadenia %s:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Požiadavka na spĆ”rovanie od zariadenia %s" #: src/service/device.js:800 msgid "Reject" msgstr "OdmietnuÅ„" #: src/service/device.js:805 msgid "Accept" msgstr "PrijaÅ„" #: src/service/manager.js:114 msgid "" "Discovery has been disabled due to the number of devices on this network." msgstr "Objavenie bolo zakĆ”zanĆ© kvĆ“li počtu zariadenĆ­ na tejto sieti." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "" #: src/service/backends/lan.js:452 #, fuzzy msgid "Port already in use" msgstr "KlĆ”vesovĆ” skratka %s sa už používa" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: BatĆ©ria je plne nabitĆ”" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "Plne nabitĆ”" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, fuzzy, javascript-format msgid "%d%% Charged" msgstr "Plne nabitĆ”" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: BatĆ©ria je vybitĆ”" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "ZostĆ”va %d%%" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "SchrĆ”nka" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "OdoslaÅ„ obsah schrĆ”nky" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "PrijaÅ„ obsah schrĆ”nky" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "NĆ”jsÅ„ mĆ“j telefón" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "OvlĆ”danie myÅ”i" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "KlĆ”vesnica" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 #, fuzzy msgid "Unknown" msgstr "NeznĆ”my kontakt" #: src/service/plugins/notification.js:16 #, fuzzy msgid "Share notifications with the paired device" msgstr "StĆ­Å”enie oznĆ”menĆ­ z mobilnĆ©ho zariadenia" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "ZruÅ”iÅ„ oznĆ”menie" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "ZavrieÅ„ oznĆ”menie" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "OdpovedaÅ„ na oznĆ”menie" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "AktivovaÅ„ oznĆ”menia" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Prenos zlyhal" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Zlyhalo odoslanie sĆŗboru ā€ž%sā€œ do zariadenia %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 #, fuzzy msgid "Presentation" msgstr "IntegrĆ”cia v aplikĆ”cii SĆŗbory" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "SpustiÅ„ prĆ­kazy" #: src/service/plugins/runcommand.js:13 msgid "" "Run commands on your paired device or let the device run predefined commands " "on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "PripojiÅ„" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "OdpojiÅ„" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "ZdieľaÅ„" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s nemĆ” povolenĆ© nahrĆ”vanie sĆŗborov" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "Prebieha prenos sĆŗboru" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "PrijĆ­ma sa ā€ž%sā€œ zo zariadenia %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Prenos ĆŗspeÅ”ný" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "Prijatý sĆŗbor ā€ž%sā€œ zo zariadenia %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "OtvoriÅ„ priečinok" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "OtvoriÅ„ sĆŗbor" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Zlyhalo prijatie sĆŗboru ā€ž%sā€œ zo zariadenia %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Text zdieľaný zariadenĆ­m %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Odosiela sa sĆŗbor ā€ž%sā€œ do zariadenia %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "Odoslaný sĆŗbor ā€ž%sā€œ do zariadenia %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Odoslanie sĆŗborov do zariadenia %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "OtvoriÅ„ po dokončenĆ­" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "OdoÅ”le odkaz do zariadenia %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "NovĆ” SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "OdpovedaÅ„ na SMS" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "ZdieľaÅ„ SMS" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "SystĆ©movĆ” hlasitosÅ„" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 #, fuzzy msgid "PulseAudio not found" msgstr "Chyba systĆ©mu PulseAudio" #: src/service/plugins/telephony.js:14 msgid "" "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "StĆ­Å”iÅ„ hovor" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "NeznĆ”my kontakt" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "PrichĆ”dzajĆŗci hovor" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "OdchĆ”dzajĆŗci hovor" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Fax" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "PrĆ”ca" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Mobil" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Domov" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "OdoslaÅ„ na čƭslo %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "PrĆ”ve teraz" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Včera惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minĆŗta" msgstr[1] "%d minĆŗty" msgstr[2] "%d minĆŗt" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "SkupinovĆ” sprĆ”va" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Vy: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "A jeden ďalŔí kontakt" msgstr[1] "A %d ďalÅ”ie kontakty" msgstr[2] "A %d ďalŔích kontaktov" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (Odhaduje sa…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d∶%02d do plnĆ©ho nabitia)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (ZostĆ”va %d∶%02d)" #: src/shell/notification.js:54 msgid "Reply" msgstr "OdpovedaÅ„" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "" "Zdieľanie odkazov s aplikĆ”ciou GSConnect, priamo cez prehliadač alebo formou " "SMS." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Služba nedostupnĆ”" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "OtvoriÅ„ v prehliadači" #~ msgid "Add" #~ msgstr "PridaÅ„" #~ msgid "On" #~ msgstr "ZapnutĆ©" #~ msgid "Off" #~ msgstr "VypnutĆ©" #~ msgid "Set Shortcut" #~ msgstr "Nastavenie skratky" #~ msgid "Authentication Failure" #~ msgstr "Zlyhalo overenie totožnosti" #~ msgid "Network Error" #~ msgstr "SieÅ„ovĆ” chyba" #~ msgid "Keyboard not ready" #~ msgstr "KlĆ”vesnica nie je pripravenĆ”" #~ msgid "All files" #~ msgstr "VÅ”etky sĆŗbory" #~ msgid "Camera pictures" #~ msgstr "SnĆ­mky fotoaparĆ”tu" #, javascript-format #~ msgid "%d hour" #~ msgid_plural "%d hours" #~ msgstr[0] "%d hodina" #~ msgstr[1] "%d hodiny" #~ msgstr[2] "%d hodĆ­n" #, javascript-format #~ msgid "Until %s (%s)" #~ msgstr "Do %s (%s)" #~ msgid "Do Not Disturb" #~ msgstr "NevyruÅ”ovaÅ„" #~ msgid "Until you turn off Do Not Disturb" #~ msgstr "Pokiaľ nevypnete funkciu NeruÅ”iÅ„" #~ msgid "Done" #~ msgstr "Hotovo" #~ msgid "Command Shortcuts" #~ msgstr "Skratky prĆ­kazov" #~ msgid "Delete" #~ msgstr "OdstrĆ”niÅ„" #~ msgid "Delete this device" #~ msgstr "OdstrĆ”nenie tohoto zariadenia" #~ msgid "Unpair and remove all settings and files" #~ msgstr "ZruŔí pĆ”rovanie a odstrĆ”ni vÅ”etky nastavenia a sĆŗbory" #~ msgid "Debugger" #~ msgstr "Ladiaci nĆ”stroj" #~ msgid "About" #~ msgstr "O aplikĆ”cii" #~ msgid "Switch to Bluetooth" #~ msgstr "PrepnĆŗÅ„ na Bluetooth" #~ msgid "Switch to LAN" #~ msgstr "PrepnĆŗÅ„ na LAN" #~ msgid "Appearance" #~ msgstr "Vzhľad" #~ msgid "Discoverable" #~ msgstr "ObjaviteľnĆ”" #~ msgid "Restart Service" #~ msgstr "ReÅ”tartovaÅ„ službu" #~ msgid "Settings" #~ msgstr "Nastavenia" #~ msgid "Remote Filesystems" #~ msgstr "VzdialenĆ© sĆŗborovĆ© systĆ©my" #~ msgid "Extended Keyboard Support" #~ msgstr "RozŔírenĆ” podpora klĆ”vesnice" #~ msgid "Additional Features" #~ msgstr "DodatočnĆ© funkcie" #~ msgid "KDE Connect" #~ msgstr "KDE Connect" #~ msgid "Click to open preferences" #~ msgstr "KliknutĆ­m otvorĆ­te nastavenia" #~ msgid "%s Plugin Failed To Load" #~ msgstr "Zlyhalo načƭtanie zĆ”suvnĆ©ho modulu %s" #~ msgid "GSConnect: %s" #~ msgstr "GSConnect: %s" #~ msgid "Reconnect" #~ msgstr "Znovu pripojiÅ„" #~ msgid "Additional Software Required" #~ msgstr "Vyžaduje sa dodatočný softvĆ©r" #~ msgid "Starting Transfer" #~ msgstr "SpúŔńa sa prenos" #~ msgid "Select a contact or number" #~ msgstr "Vyberte kontakt alebo čƭslo" gnome-shell-extension-gsconnect-50/po/sr.po000066400000000000000000001153711421543444100211210ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: org.gnome.Shell.Extensions.GSConnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2019-09-25 15:46+0200\n" "Last-Translator: ДлобоГан Терзић \n" "Language-Team: \n" "Language: sr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.2.1\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" # Leaving the name in original form for About dialog. #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 #, fuzzy msgid "KDE Connect implementation for GNOME" msgstr "ŠŸŠ¾Ń‚ŠæŃƒŠ½Š° ŠøŠ¼ŠæŠ»ŠµŠ¼ŠµŠ½Ń‚Š°Ń†ŠøŃ˜Š° ŠšŠ”Š• ŠšŠ¾Š½ŠµŠŗŃ‚Š° за Гном" # Leaving the name in original form for About dialog. #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 #, fuzzy msgid "GSConnect Team" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "" "GSConnect is a complete implementation of KDE Connect especially for GNOME " "Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team " "has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "" "With GSConnect you can securely connect to mobile devices and other desktops " "to:" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 #, fuzzy msgid "Sync contacts" msgstr "ŠŠµŠ¼Š° контаката" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 #, fuzzy msgid "Sync notifications" msgstr "ŠŸŠ¾ŃˆŠ°Ń™Šø Š¾Š±Š°Š²ŠµŃˆŃ‚ŠµŃšŠµ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 #, fuzzy msgid "Control media players" msgstr "МеГијски ŠæŠ»ŠµŃ˜ŠµŃ€Šø" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 #, fuzzy msgid "Control system volume" msgstr "Дистемска Ń˜Š°Ń‡ŠøŠ½Š°" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Повежи се са…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "ŠžŃ‚ŠŗŠ°Š¶Šø" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Повежи" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "ИП аГреса" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "ŠŠµŠ¼Š° контаката" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "ŠŸŠ¾Š¼Š¾Ń›" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Унеите Š±Ń€Š¾Ń˜ телефона или име" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 #, fuzzy msgid "Other" msgstr "Š”Ń€ŃƒŠ³Š¾" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "ŠŸŠ¾ŃˆŠ°Ń™Šø ДМД" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "ŠŸŠ¾ŃˆŠ°Ń™Šø" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Š£Ń€ŠµŃ’Š°Ń˜ није повезан" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "ŠŸŠ¾ŃˆŠ°Ń™Šø ŠæŠ¾Ń€ŃƒŠŗŃƒ" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Унесите ŠæŠ¾Ń€ŃƒŠŗŃƒ" #: data/ui/messaging-conversation.ui:92 #, fuzzy msgid "Message Entry" msgstr "ŠŠ¾Š²Š° ŠæŠ¾Ń€ŃƒŠŗŠ°" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "ŠŸŠ¾Ń€ŃƒŠŗŠµ" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "ŠŠ¾Š²Šø разговор" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "ŠŠµŠ¼Š° разговора" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "ŠŠøŃ˜Šµ изабран разговор" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Š˜Š·Š°Š±ŠµŃ€ŠøŃ‚Šµ или започните разговор" #: data/ui/preferences-command-editor.ui:7 #, fuzzy msgid "Edit Command" msgstr "ŠŠ°Ń€ŠµŠ“Š±Šµ" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Дними" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Име" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "КоманГна линија" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Š˜Š·Š°Š±ŠµŃ€ŠøŃ‚Šµ ŠøŠ·Š²Ń€ŃˆŠ½Šø Ń„Š°Ń˜Š»" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "ŠžŃ‚Š²Š¾Ń€Šø" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "РаГна ŠæŠ¾Š²Ń€Ńˆ" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "ŠšŠ°Š¼ŠµŃ€Š°" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Š”ŠøŠ½Ń…Ń€Š¾Š½ŠøŠ·Š°Ń†ŠøŃ˜Š° оставе" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "МеГијски ŠæŠ»ŠµŃ˜ŠµŃ€Šø" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Миш Šø Ń‚Š°ŃŃ‚Š°Ń‚ŃƒŃ€Š°" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "ŠˆŠ°Ń‡ŠøŠ½Š° звука" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Фајлови" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "ŠŸŃ€ŠøŠ¼Šø Ń„Š°Ń˜Š»Š¾Š²Šµ" #: data/ui/preferences-device-panel.ui:497 #, fuzzy msgid "Save files to" msgstr "ŠŸŠ¾ŃˆŠ°Ń™Šø Ń„Š°Ń˜Š»Š¾Š²Šµ на %s" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Š”ŠµŃ™ŠµŃšŠµ" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Š‘Š°Ń‚ŠµŃ€ŠøŃ˜Š° ŃƒŃ€ŠµŃ’Š°Ń˜Š°" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "ŠžŠ±Š°Š²ŠµŃˆŃ‚ŠµŃšŠµ о скоро ŠæŃ€Š°Š·Š½Š¾Ń˜ Š±Š°Ń‚ŠµŃ€ŠøŃ˜Šø" #: data/ui/preferences-device-panel.ui:699 #, fuzzy msgid "Charged Up to Custom Level Notification" msgstr "ŠžŠ±Š°Š²ŠµŃˆŃ‚ŠµŃšŠµ о ŠæŠ¾Ń‚ŠæŃƒŠ½Š¾Ń˜ Š½Š°ŠæŃƒŃšŠµŠ½Š¾ŃŃ‚Šø" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "ŠžŠ±Š°Š²ŠµŃˆŃ‚ŠµŃšŠµ о ŠæŠ¾Ń‚ŠæŃƒŠ½Š¾Ń˜ Š½Š°ŠæŃƒŃšŠµŠ½Š¾ŃŃ‚Šø" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Š‘Š°Ń‚ŠµŃ€ŠøŃ˜Š° система" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Дели ŃŃ‚ŃŃ‚ŠøŃŃ‚ŠøŠŗŃƒ" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Š‘Š°Ń‚ŠµŃ€ŠøŃ˜Š°" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "ŠŠ°Ń€ŠµŠ“Š±Šµ" #: data/ui/preferences-device-panel.ui:1025 #, fuzzy msgid "Add Command" msgstr "ŠŠ°Ń€ŠµŠ“Š±Šµ" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Дели Š¾Š±Š°Š²ŠµŃˆŃ‚ŠµŃšŠ°" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "ŠŸŃ€Š¾Š³Ń€Š°Š¼Šø" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "ŠžŠ±Š°Š²ŠµŃˆŃ‚ŠµŃšŠ°" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "ŠšŠ¾Š½Ń‚Š°ŠŗŃ‚Šø" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Долазни позиви" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "ŠˆŠ°Ń‡ŠøŠ½Š°" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "ŠŸŠ°ŃƒŠ·ŠøŃ€Š°Ń˜ меГије" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Š¢ŠµŠŗŃƒŃ›ŠøŠø позиви" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Š£Ń‚ŠøŃˆŠ°Ń˜ микрофон" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Š¢ŠµŠ»ŠµŃ„Š¾Š½ŠøŃ˜Š°" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "ŠŸŃ€ŠµŃ‡ŠøŃ†Šµ Ń€Š°Š“ŃšŠø" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Š ŠµŃŠµŃ‚ŃƒŃ˜ све…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "ŠŸŃ€ŠµŃ‡ŠøŃ†Šµ" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "ŠŸŃ€ŠøŠŗŃ™ŃƒŃ‡Ń†Šø" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "ŠŸŃ€Š¾Š±Š½Š¾" #: data/ui/preferences-device-panel.ui:2000 #, fuzzy msgid "Device Cache" msgstr "ŠŠ°Š·ŠøŠ² ŃƒŃ€ŠµŃ’Š°Ń˜Š°" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "ŠŠ°ŃŠ»ŠµŠ“Š½Š° ŠæŠ¾Š“Ń€ŃˆŠŗŠ° за ДМД" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "ŠŠ°ŠæŃ€ŠµŠ“Š½Š¾" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "ŠŸŃ€ŠµŃ‡ŠøŃ†Šµ Ń‚Š°ŃŃ‚Š°Ń‚ŃƒŃ€Šµ" #: data/ui/preferences-device-panel.ui:2513 #, fuzzy msgid "Device Settings" msgstr "ПоГешавање" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Упари" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "Š£Ń€ŠµŃ’Š°Ń˜ је распарен" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "ŠœŠ¾Š¶ŠµŃ‚Šµ поГесити овај ŃƒŃ€ŠµŃ’Š°Ń˜ пре ŃƒŠæŠ°Ń€ŠøŠ²Š°ŃšŠ°" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Š˜Š½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ˜Šµ о ŃˆŠøŃ„Ń€Š¾Š²Š°ŃšŃƒ" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Распари" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "ŠŠ° ŃƒŃ€ŠµŃ’Š°Ń˜" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Да ŃƒŃ€ŠµŃ’Š°Ń˜Š°" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "ŠŠøŃˆŃ‚Š°" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Š£Ń‚ŠøŃˆŠ°Ń˜" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Š£Ń‚ŠøŃˆŠ°Ń˜" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "ŠŸŠ¾ŃŃ‚Š°Š²Šø" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "" "ŠŸŃ€ŠøŃ‚ŠøŃŠ½ŠøŃ‚Šµ Есц Га откажете или ŠŸŠ¾Š²Ń€Š°Ń‚ник Га Ń€ŠµŃŠµŃ‚ŃƒŃ˜ŠµŃ‚Šµ ŠæŃ€ŠµŃ‡ŠøŃ†Ńƒ Ń‚Š°ŃŃ‚Š°Ń‚ŃƒŃ€Šµ." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "ŠŠ°Š·ŠøŠ² ŃƒŃ€ŠµŃ’Š°Ń˜Š°" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_ŠŸŃ€ŠµŠøŠ¼ŠµŠ½ŃƒŃ˜" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "ŠžŃŠ²ŠµŠ¶Šø" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "ПоГешавање" #: data/ui/preferences-window.ui:159 #, fuzzy msgid "Service Menu" msgstr "Дервис" #: data/ui/preferences-window.ui:182 #, fuzzy msgid "Device Menu" msgstr "ŠŠ°Š·ŠøŠ² ŃƒŃ€ŠµŃ’Š°Ń˜Š°" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "УреГи назив ŃƒŃ€ŠµŃ’Š°Ń˜Š°" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Š£Ń€ŠµŃ’Š°Ń˜Šø" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Тражим ŃƒŃ€ŠµŃ’Š°Ń˜Šµā€¦" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "ДоГаци за преглеГаче" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "ŠžŠ¼Š¾Š³ŃƒŃ›Šø" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "ŠžŠ²Š°Ń˜ ŃƒŃ€ŠµŃ’Š°Ń˜ је невиГљив Š½ŠµŃƒŠæŠ°Ń€ŠµŠ½ŠøŠ¼ ŃƒŃ€ŠµŃ’Š°Ń˜ŠøŠ¼Š°" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "ŠžŃ‚ŠŗŃ€ŠøŠ²Š°ŃšŠµ је Š¾Š½ŠµŠ¼Š¾Š³ŃƒŃ›ŠµŠ½Š¾" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Режим приказа" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Панел" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "ŠšŠ¾Ń€ŠøŃŠ½ŠøŃ‡ŠŗŠø мени" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "ŠŠ°ŠæŃ€Š°Š²Šø Гневник за ŠæŠ¾Š“Ń€ŃˆŠŗŃƒ" # Leaving the name in original form for About dialog. #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "Šž ŠæŃ€Š¾Š³Ń€Š°Š¼Ńƒ" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Š˜Š·Š°Š±ŠµŃ€ŠøŃ‚Šµ ŃƒŃ€ŠµŃ’Š°Ń˜" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Š˜Š·Š°Š±ŠµŃ€ŠøŃ‚Šµ" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "ŠŠµŠ¼Š° нађених ŃƒŃ€ŠµŃ’Š°Ń˜Š°" #: data/ui/service-device-chooser.ui:111 #, fuzzy msgid "Device List" msgstr "Š£Ń€ŠµŃ’Š°Ń˜Šø" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "ŠŸŃ€ŠøŃ˜Š°Š²Šø" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "" #: data/ui/service-error-dialog.ui:84 msgid "" "GSConnect encountered an unexpected error. Please report the problem and " "include any information that may help." msgstr "" #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "ŠŸŠ¾ŃˆŠ°Ń™Šø на мобилни ŃƒŃ€ŠµŃ’Š°Ń˜" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Мобилни ŃƒŃ€ŠµŃ’Š°Ń˜Šø" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "" #: src/extension.js:232 #, fuzzy, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d је повезан" msgstr[1] "%d је повезан" msgstr[2] "%d је повезан" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "УреГи" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Уклони" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "ŠžŠ½ŠµŠ¼Š¾Š³ŃƒŃ›ŠµŠ½" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Унесите нову ŠæŃ€ŠµŃ‡ŠøŃ†Ńƒ Га замените %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s је спреман за ŃƒŠæŠ¾Ń‚Ń€ŠµŠ±Ńƒ" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "ŠŸŠ¾Ń‚ŠæŃƒŠ½Š° ŠøŠ¼ŠæŠ»ŠµŠ¼ŠµŠ½Ń‚Š°Ń†ŠøŃ˜Š° ŠšŠ”Š• ŠšŠ¾Š½ŠµŠŗŃ‚Š° за Гном" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "ДлобоГан Терзић (githzerai06@gmail.com)" #: src/preferences/service.js:411 msgid "" "Debug messages are being logged. Take any steps necessary to reproduce a " "problem then review the log." msgstr "" "ŠŸŠ¾Ń€ŃƒŠŗŠµ за ŠøŃŠæŃ€Š°Š²Ń™Š°ŃšŠµ Š³Ń€ŠµŃˆŠ°ŠŗŠ° се бележе. ŠŸŃ€ŠµŠ“ŃƒŠ·Š¼ŠøŃ‚Šµ неопхоГне кораке Га " "поново призвеГете проблем Šø ŠæŃ€ŠµŠ³ŠµŠ»ŠµŠ“Š°Ń˜Ń‚Šµ Гневник." #: src/preferences/service.js:414 msgid "Review Log" msgstr "ŠŸŃ€ŠµŠ³Š»ŠµŠ“ Гневника" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Лаптоп" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "ŠŸŠ°Š¼ŠµŃ‚Š½Šø телефон" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Таблет" #: src/preferences/service.js:488 msgid "Television" msgstr "Телевизија" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Распарен" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "ŠŠµŠæŠ¾Š²ŠµŠ·Š°Š½" #: src/preferences/service.js:518 msgid "Connected" msgstr "Повезан" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "Чекам на сервис…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "ŠšŠ»ŠøŠŗŠ½ŠøŃ‚Šµ за помоћ у Š¾Ń‚ŠŗŠ»Š°ŃšŠ°ŃšŃƒ" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "ŠšŠ»ŠøŠŗŠ½ŠøŃ‚Šµ за више Гетаља" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Š‘ŠøŃ€Š°Ń˜ Š±Ń€Š¾Ń˜" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Дели Ń„Š°Ń˜Š»" #: src/service/daemon.js:351 #, fuzzy msgid "List available devices" msgstr "ŠŠøŃ˜Šµ Š“Š¾ŃŃ‚ŃƒŠæŠ½Š¾" #: src/service/daemon.js:360 #, fuzzy msgid "List all devices" msgstr "Мобилни ŃƒŃ€ŠµŃ’Š°Ń˜Šø" #: src/service/daemon.js:369 #, fuzzy msgid "Target Device" msgstr "ŠŠ° ŃƒŃ€ŠµŃ’Š°Ń˜" #: src/service/daemon.js:411 #, fuzzy msgid "Message Body" msgstr "ŠŠ¾Š²Š° ŠæŠ¾Ń€ŃƒŠŗŠ°" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "ŠŸŠ¾ŃˆŠ°Ń™Šø Š¾Š±Š°Š²ŠµŃˆŃ‚ŠµŃšŠµ" #: src/service/daemon.js:432 #, fuzzy msgid "Notification App Name" msgstr "ŠžŠ±Š°Š²ŠµŃˆŃ‚ŠµŃšŠ°" #: src/service/daemon.js:441 #, fuzzy msgid "Notification Body" msgstr "ŠžŠ±Š°Š²ŠµŃˆŃ‚ŠµŃšŠ°" #: src/service/daemon.js:450 #, fuzzy msgid "Notification Icon" msgstr "ŠžŠ±Š°Š²ŠµŃˆŃ‚ŠµŃšŠ°" #: src/service/daemon.js:459 #, fuzzy msgid "Notification ID" msgstr "ŠžŠ±Š°Š²ŠµŃˆŃ‚ŠµŃšŠ°" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Š¤Š¾Ń‚Š¾Š³Ń€Š°Ń„ŠøŃ˜Šµ" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Пинг" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Позвони" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "ПоГели везу" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Дели текст" #: src/service/daemon.js:528 #, fuzzy msgid "Show release version" msgstr "Š”Š¾Š“Š°Ń˜Ń‚Šµ особе Га започнете разговор" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "ŠŠøŃ˜Šµ Š“Š¾ŃŃ‚ŃƒŠæŠ½Š¾" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Š‘Š»ŃƒŃ‚ŃƒŃ‚ ŃƒŃ€ŠµŃ’Š°Ń˜ на %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "ŠžŃ‚ŠøŃŠ°Šŗ %s:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Захтев за ŃƒŠæŠ°Ń€ŠøŠ²Š°ŃšŠµ оГ %s" #: src/service/device.js:800 msgid "Reject" msgstr "ŠžŠ“Š±ŠøŃ˜" #: src/service/device.js:805 msgid "Accept" msgstr "ŠŸŃ€ŠøŃ…Š²Š°Ń‚Šø" #: src/service/manager.js:114 msgid "" "Discovery has been disabled due to the number of devices on this network." msgstr "ŠžŃ‚ŠŗŃ€ŠøŠ²Š°ŃšŠµ је Š¾Š½ŠµŠ¼Š¾Š³ŃƒŃ›ŠµŠ½Š¾ услеГ Š±Ń€Š¾Ń˜Š° ŃƒŃ€ŠµŃ’Š°Ń˜Š° у овој мрежи." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "" #: src/service/backends/lan.js:452 #, fuzzy msgid "Port already in use" msgstr "%s је спреман за ŃƒŠæŠ¾Ń‚Ń€ŠµŠ±Ńƒ" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, fuzzy, javascript-format msgid "%s: Battery is full" msgstr "%s: Š±Š°Ń‚ŠµŃ€ŠøŃ˜Š° је при ŠŗŃ€Š°Ń˜Ńƒ" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "ŠŸŠ¾Ń‚ŠæŃƒŠ½Š¾ пуна" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, fuzzy, javascript-format msgid "%d%% Charged" msgstr "ŠŸŠ¾Ń‚ŠæŃƒŠ½Š¾ пуна" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: Š±Š°Ń‚ŠµŃ€ŠøŃ˜Š° је при ŠŗŃ€Š°Ń˜Ńƒ" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% ŠæŃ€ŠµŠ¾ŃŃ‚Š°Ń˜Šµ" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "ŠžŃŃ‚Š°Š²Š°" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "Длање у Š¾ŃŃ‚Š°Š²Ńƒ" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "Š”Š¾Š²Š»Š°Ń‡ŠµŃšŠµ ŠøŠ· оставе" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "ŠŠ°Ń’Šø ми телефон" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "ПоГлога за миша" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Š¢Š°ŃŃ‚Š°Ń‚ŃƒŃ€Š°" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "МПРИД" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 #, fuzzy msgid "Unknown" msgstr "ŠŠµŠæŠ¾Š·Š½Š°Ń‚ контакт" #: src/service/plugins/notification.js:16 #, fuzzy msgid "Share notifications with the paired device" msgstr "Š£Ń‚ŠøŃˆŠ°Ń˜ Š¾Š±Š°Š²ŠµŃˆŃ‚ŠµŃšŠ° мобилиних ŃƒŃ€ŠµŃ’Š°Ń˜Š°" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "ŠžŃ‚ŠŗŠ°Š¶Šø Š¾Š±Š°Š²ŠµŃˆŃ‚ŠµŃšŠµ" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Затвори Š¾Š±Š°Š²ŠµŃˆŃ‚ŠµŃšŠµ" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "ŠžŠ±Š°Š²ŠµŃˆŃ‚ŠµŃšŠµ о Š¾Š“Š“Š³Š¾Š²Š¾Ń€Ńƒ" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "ŠŠŗŃ‚ŠøŠ²ŠøŃ€Š°Ń˜ Š¾Š±Š°Š²ŠµŃˆŃ‚ŠµŃšŠµ" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "ŠŸŃ€ŠµŠ½Š¾Ń није успео" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "ŠŠµŃƒŃŠæŠµŠ»Š¾ слање ā€ž%sā€œ на %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Пинг: %s" #: src/service/plugins/presenter.js:10 #, fuzzy msgid "Presentation" msgstr "Š£Š³Ń€Š°Ń’ŠøŠ²Š°ŃšŠµ у Фајлове" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Š˜Š·Š²Ń€ŃˆŠ°Š²Š°ŃšŠµ нареби" #: src/service/plugins/runcommand.js:13 msgid "" "Run commands on your paired device or let the device run predefined commands " "on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "ДФТП" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "ŠœŠ¾Š½Ń‚ŠøŃ€Š°Ń˜" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Š”ŠµŠ¼Š¾Š½Ń‚ŠøŃ€Š°Ń˜" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Š”ŠµŃ™ŠµŃšŠµ" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 #, fuzzy msgid "Transferring File" msgstr "ŠŸŃ€ŠµŠ½Š¾Ń није успео" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "ŠŸŃ€ŠøŠ¼Š°Š¼ ā€ž%sā€œ оГ %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Успешан пренос" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "ŠŸŃ€ŠøŠ¼ŠøŃ… ā€ž%sā€œ оГ %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "ŠžŃ‚Š²Š¾Ń€Šø Ń„Š°ŃŃ†ŠøŠŗŠ»Ńƒ" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "ŠžŃ‚Š²Š¾Ń€Šø Ń„Š°Ń˜Š»" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "ŠŠµŃƒŃŠæŠµŃˆŠ°Š½ ŠæŃ€ŠøŃ˜ŠµŠ¼ ā€ž%sā€œ оГ %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Дељени текст оГ %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Длање ā€ž%sā€œ за %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "ŠŸŠ¾ŃŠ»Š°Ń… ā€ž%sā€œ за %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "ŠŸŠ¾ŃˆŠ°Ń™Šø Ń„Š°Ń˜Š»Š¾Š²Šµ на %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 #, fuzzy msgid "Open when done" msgstr "ŠžŃ‚Š²Š¾Ń€Šø у ŠæŃ€ŠµŠ³Š»ŠµŠ“Š°Ń‡Ńƒ" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "ŠŸŠ¾ŃˆŠ°Ń™Šø везе на %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "ДМД" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "ŠŠ¾Š²Šø ДМД (УРИ)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "ŠžŠ“Š³Š¾Š²Š¾Ń€Šø на ДМД" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "Дели ДМД" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Дистемска Ń˜Š°Ń‡ŠøŠ½Š°" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 #, fuzzy msgid "PulseAudio not found" msgstr "Š“Ń€ŠµŃˆŠŗŠ° ПулсауГија" #: src/service/plugins/telephony.js:14 msgid "" "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Š£Ń‚ŠøŃˆŠ°Ń˜ позив" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "ŠŠµŠæŠ¾Š·Š½Š°Ń‚ контакт" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Долазни позив" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Š¢ŠµŠŗŃƒŃ›Šø позив" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Факс" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 #, fuzzy msgid "Work" msgstr "Посао" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 #, fuzzy msgid "Mobile" msgstr "Мобилни" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 #, fuzzy msgid "Home" msgstr "ŠšŃƒŃ›Š½Šø" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "ŠŸŠ¾ŃˆŠ°Ń™Šø на %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Управо саГа" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "ŠˆŃƒŃ‡Šµćƒ»%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d Š¼ŠøŠ½ŃƒŃ‚" msgstr[1] "%d Š¼ŠøŠ½ŃƒŃ‚Š°" msgstr[2] "%d Š¼ŠøŠ½ŃƒŃ‚Š°" #: src/service/ui/messaging.js:750 #, fuzzy msgid "Group Message" msgstr "ŠŠ¾Š²Š° ŠæŠ¾Ń€ŃƒŠŗŠ°" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "" msgstr[1] "" msgstr[2] "" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (ŠŸŃ€Š¾Ń†ŠµŃšŃƒŃ˜ŠµŠ¼ā€¦)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d∶%02d Го пуне)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d∶%02d ŠæŃ€ŠµŠ¾ŃŃ‚Š°Ń˜Šµ)" #: src/shell/notification.js:54 msgid "Reply" msgstr "ŠžŠ“Š³Š¾Š²Š¾Ń€Šø" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Дели везе Š“Š”ŠšŠ¾Š½ŠµŠŗŃ‚Š¾Š¼, Гиректно у преглГач или ŠæŃƒŃ‚ем ДМД-а." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Дервис није Š“Š¾ŃŃ‚ŃƒŠæŠ°Š½" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "ŠžŃ‚Š²Š¾Ń€Šø у ŠæŃ€ŠµŠ³Š»ŠµŠ“Š°Ń‡Ńƒ" #~ msgid "Add" #~ msgstr "Š”Š¾Š“Š°Ń˜" #~ msgid "On" #~ msgstr "Š£ŠŗŃ™ŃƒŃ‡ŠµŠ½" #~ msgid "Off" #~ msgstr "Š˜ŃŠŗŃ™ŃƒŃ‡ŠµŠ½" #~ msgid "Set Shortcut" #~ msgstr "ŠŸŠ¾ŃŃ‚Š°Š²Šø ŠæŃ€ŠµŃ‡ŠøŃ†Ńƒ" #~ msgid "Authentication Failure" #~ msgstr "Š“Ń€ŠµŃˆŠŗŠ° Š°ŃƒŃ‚ŠµŠ½Ń‚ŠøŃ„ŠøŠŗŠ°Ń†Ń˜Šµ" #~ msgid "Network Error" #~ msgstr "Š“Ń€ŠµŃˆŠŗŠ° мреже" #~ msgid "Keyboard not ready" #~ msgstr "Š¢Š°ŃŃ‚Š°Ń‚ŃƒŃ€Š° није спремна" #~ msgid "All files" #~ msgstr "Дви Ń„Š°Ń˜Š»Š¾Š²Šø" #~ msgid "Camera pictures" #~ msgstr "Длике са камере" #, javascript-format #~ msgid "%d hour" #~ msgid_plural "%d hours" #~ msgstr[0] "%d час" #~ msgstr[1] "%d часа" #~ msgstr[2] "%d часова" #, javascript-format #~ msgid "Until %s (%s)" #~ msgstr "До %s (%s)" #~ msgid "Do Not Disturb" #~ msgstr "ŠŠµ ŃƒŠ·Š½ŠµŠ¼ŠøŃ€Š°Š²Š°Ń˜" #~ msgid "Until you turn off Do Not Disturb" #~ msgstr "Док не ŠøŃŠŗŃ™ŃƒŃ‡ŠøŠ¼ ŠŠµ ŃƒŠ·Š½ŠµŠ¼ŠøŃ€Š°Š²Š°Ń˜" #~ msgid "Done" #~ msgstr "Готово" #~ msgid "Command Shortcuts" #~ msgstr "ŠŸŃ€ŠµŃ‡ŠøŃ†Šµ нареГби" #~ msgid "Delete" #~ msgstr "ŠžŠ±Ń€ŠøŃˆŠø" #~ msgid "Delete this device" #~ msgstr "ŠžŠ±Ń€ŠøŃˆŠø овај ŃƒŃ€ŠµŃ’Š°Ń˜" #~ msgid "Unpair and remove all settings and files" #~ msgstr "Распари Šø уклони све поставке Šø Ń„Š°Ń˜Š»Š¾Š²Šµ" #~ msgid "Debugger" #~ msgstr "Š˜ŃŠæŃ€Š°Š²Ń™Š°Ń‡ Š³Ń€ŠµŃˆŠ°ŠŗŠ°" #~ msgid "About" #~ msgstr "Šž ŠæŃ€Š¾Š³Ń€Š°Š¼Ńƒ" #, fuzzy #~ msgid "Switch to Bluetooth" #~ msgstr "Повежи Š‘Š»ŃƒŃ‚ŃƒŃ‚Š¾Š¼" #~ msgid "Appearance" #~ msgstr "ИзглеГ" #~ msgid "Discoverable" #~ msgstr "ŠžŃ‚ŠŗŃ€ŠøŠ²Š°ŃšŠµ је Š¾Š¼Š¾Š³ŃƒŃ›ŠµŠ½Š¾" #~ msgid "Restart Service" #~ msgstr "Поново покрени сервис" #~ msgid "Settings" #~ msgstr "ПоГешавање" #~ msgid "Remote Filesystems" #~ msgstr "УГаљени системи Ń„Š°Ń˜Š»Š¾Š²Š°" #~ msgid "Sound Effects" #~ msgstr "Š—Š²ŃƒŃ‡Š½Šø ефекти" #~ msgid "Extended Keyboard Support" #~ msgstr "ŠŸŃ€Š¾ŃˆŠøŃ€ŠµŠ½Š° ŠæŠ¾Š“Ń€ŃˆŠŗŠ° за Ń‚Š°ŃŃ‚Š°Ń‚ŃƒŃ€Ńƒ" #~ msgid "Desktop Contacts" #~ msgstr "ŠšŠ¾Š½Ń‚Š°ŠŗŃ‚Šø са Гесктопа" #~ msgid "Additional Features" #~ msgstr "ДоГатне Š¼Š¾Š³ŃƒŃ›Š½Š¾ŃŃ‚Šø" # Leaving the name in original form for About dialog. #~ msgid "KDE Connect" #~ msgstr "KDE Connect" #~ msgid "Click to open preferences" #~ msgstr "ŠšŠ»ŠøŠŗŠ½ŠøŃ‚Šµ Га отворите поставке" #~ msgid "Additional Software Required" #~ msgstr "ŠŠµŠ¾ŠæŃ…Š¾Š“Š°Š½ је ГоГатан софтвер" #~ msgid "%s Plugin Failed To Load" #~ msgstr "ŠŠµŃƒŃŠæŠµŠ»Š¾ ŃƒŃ‡ŠøŃ‚Š°Š²Š°ŃšŠµ ŠæŃ€ŠŗŃ™ŃƒŃ‡ŠŗŠ° %s" #~ msgid "Wayland Not Supported" #~ msgstr "Š’ŠµŃ˜Š»Š°Š½Š“ није поГржан" #~ msgid "Remote input not supported on Wayland" #~ msgstr "Унос на Š“Š°Ń™ŠøŠ½Ńƒ није поГржан поГ Š’ŠµŃ˜Š»Š°Š½Š“Š¾Š¼" # Leaving the name in original form for About dialog. #~ msgid "GSConnect: %s" #~ msgstr "GSConnect: %s" #~ msgid "Reconnect" #~ msgstr "Поново повежи" #~ msgid "Locate Device" #~ msgstr "Š›Š¾Ń†ŠøŃ€Š°ŃšŠµ ŃƒŃ€ŠµŃ’Š°Ń˜Š°" #~ msgid "%s asked to locate this device" #~ msgstr "%s тражи овај ŃƒŃ€ŠµŃ’Š°Ń˜" #~ msgid "Found" #~ msgstr "ŠŠ°Ń’ŠµŠ½" #~ msgid "Starting Transfer" #~ msgstr "Š—Š°ŠæŠ¾Ń‡ŠøŃšŠµŠ¼ пренос" gnome-shell-extension-gsconnect-50/po/sr@latin.po000066400000000000000000001073561421543444100222550ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: org.gnome.Shell.Extensions.GSConnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2019-09-25 15:46+0200\n" "Last-Translator: Slobodan Terzić \n" "Language-Team: \n" "Language: sr@latin\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.2.1\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" # Leaving the name in original form for About dialog. #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 #, fuzzy msgid "KDE Connect implementation for GNOME" msgstr "Potpuna implementacija KDE Konekta za Gnom" # Leaving the name in original form for About dialog. #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 #, fuzzy msgid "GSConnect Team" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "" "GSConnect is a complete implementation of KDE Connect especially for GNOME " "Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team " "has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "" "With GSConnect you can securely connect to mobile devices and other desktops " "to:" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 #, fuzzy msgid "Sync contacts" msgstr "Nema kontakata" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 #, fuzzy msgid "Sync notifications" msgstr "PoÅ”alji obaveÅ”tenje" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 #, fuzzy msgid "Control media players" msgstr "Medijski plejeri" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 #, fuzzy msgid "Control system volume" msgstr "Sistemska jačina" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Poveži se sa…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Otkaži" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Poveži" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP adresa" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "Nema kontakata" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Pomoć" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Uneite broj telefona ili ime" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Drugo" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "PoÅ”alji SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "PoÅ”alji" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Uređaj nije povezan" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "PoÅ”alji poruku" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Unesite poruku" #: data/ui/messaging-conversation.ui:92 #, fuzzy msgid "Message Entry" msgstr "Nova poruka" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Poruke" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Novi razgovor" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Nema razgovora" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Nije izabran razgovor" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Izaberite ili započnite razgovor" #: data/ui/preferences-command-editor.ui:7 #, fuzzy msgid "Edit Command" msgstr "Naredbe" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Snimi" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Ime" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Komandna linija" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Izaberite izvrÅ”ni fajl" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Otvori" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Radna povrÅ”" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "Kamera" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Sinhronizacija ostave" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Medijski plejeri" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "MiÅ” i tastatura" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Jačina zvuka" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Fajlovi" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Primi fajlove" #: data/ui/preferences-device-panel.ui:497 #, fuzzy msgid "Save files to" msgstr "PoÅ”alji fajlove na %s" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Deljenje" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Baterija uređaja" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "ObaveÅ”tenje o skoro praznoj bateriji" #: data/ui/preferences-device-panel.ui:699 #, fuzzy msgid "Charged Up to Custom Level Notification" msgstr "ObaveÅ”tenje o potpunoj napunjenosti" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "ObaveÅ”tenje o potpunoj napunjenosti" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Baterija sistema" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Deli ststistiku" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Baterija" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Naredbe" #: data/ui/preferences-device-panel.ui:1025 #, fuzzy msgid "Add Command" msgstr "Naredbe" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Deli obaveÅ”tenja" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Programi" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "ObaveÅ”tenja" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Kontakti" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Dolazni pozivi" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Jačina" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Pauziraj medije" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Tekućii pozivi" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "UtiÅ”aj mikrofon" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Telefonija" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Prečice radnji" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Resetuj sve…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Prečice" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Priključci" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Probno" #: data/ui/preferences-device-panel.ui:2000 #, fuzzy msgid "Device Cache" msgstr "Naziv uređaja" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Nasledna podrÅ”ka za SMS" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "Napredno" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Prečice tastature" #: data/ui/preferences-device-panel.ui:2513 #, fuzzy msgid "Device Settings" msgstr "PodeÅ”avanje" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Upari" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "Uređaj je rasparen" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Možete podesiti ovaj uređaj pre uparivanja" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Informacije o Å”ifrovanju" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Raspari" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Na uređaj" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Sa uređaja" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "NiÅ”ta" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "UtiÅ”aj" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "UtiÅ”aj" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Postavi" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "" "Pritisnite Esc da otkažete ili Povratnik da resetujete prečicu tastature." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Naziv uređaja" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_Preimenuj" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Osveži" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "PodeÅ”avanje" #: data/ui/preferences-window.ui:159 #, fuzzy msgid "Service Menu" msgstr "Servis" #: data/ui/preferences-window.ui:182 #, fuzzy msgid "Device Menu" msgstr "Naziv uređaja" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Uredi naziv uređaja" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Uređaji" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Tražim uređaje…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Dodaci za pregledače" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Omogući" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Ovaj uređaj je nevidljiv neuparenim uređajima" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Otkrivanje je onemogućeno" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Režim prikaza" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Panel" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "Korisnički meni" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Napravi dnevnik za podrÅ”ku" # Leaving the name in original form for About dialog. #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "O programu" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Izaberite uređaj" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "Izaberite" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Nema nađenih uređaja" #: data/ui/service-device-chooser.ui:111 #, fuzzy msgid "Device List" msgstr "Uređaji" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Prijavi" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "" #: data/ui/service-error-dialog.ui:84 msgid "" "GSConnect encountered an unexpected error. Please report the problem and " "include any information that may help." msgstr "" #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "PoÅ”alji na mobilni uređaj" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Mobilni uređaji" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "" #: src/extension.js:232 #, fuzzy, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d je povezan" msgstr[1] "%d je povezan" msgstr[2] "%d je povezan" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Uredi" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Ukloni" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Onemogućen" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Unesite novu prečicu da zamenite %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s je spreman za upotrebu" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Potpuna implementacija KDE Konekta za Gnom" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "Slobodan Terzić (githzerai06@gmail.com)" #: src/preferences/service.js:411 msgid "" "Debug messages are being logged. Take any steps necessary to reproduce a " "problem then review the log." msgstr "" "Poruke za ispravljanje greÅ”aka se beleže. Preduzmite neophodne korake da " "ponovo prizvedete problem i pregeledajte dnevnik." #: src/preferences/service.js:414 msgid "Review Log" msgstr "Pregled dnevnika" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Laptop" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Pametni telefon" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tablet" #: src/preferences/service.js:488 msgid "Television" msgstr "Televizija" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Rasparen" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Nepovezan" #: src/preferences/service.js:518 msgid "Connected" msgstr "Povezan" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "Čekam na servis…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "Kliknite za pomoć u otklanjanju" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "Kliknite za viÅ”e detalja" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Biraj broj" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Deli fajl" #: src/service/daemon.js:351 #, fuzzy msgid "List available devices" msgstr "Nije dostupno" #: src/service/daemon.js:360 #, fuzzy msgid "List all devices" msgstr "Mobilni uređaji" #: src/service/daemon.js:369 #, fuzzy msgid "Target Device" msgstr "Na uređaj" #: src/service/daemon.js:411 #, fuzzy msgid "Message Body" msgstr "Nova poruka" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "PoÅ”alji obaveÅ”tenje" #: src/service/daemon.js:432 #, fuzzy msgid "Notification App Name" msgstr "ObaveÅ”tenja" #: src/service/daemon.js:441 #, fuzzy msgid "Notification Body" msgstr "ObaveÅ”tenja" #: src/service/daemon.js:450 #, fuzzy msgid "Notification Icon" msgstr "ObaveÅ”tenja" #: src/service/daemon.js:459 #, fuzzy msgid "Notification ID" msgstr "ObaveÅ”tenja" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Fotografije" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Pozvoni" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Podeli vezu" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Deli tekst" #: src/service/daemon.js:528 #, fuzzy msgid "Show release version" msgstr "Dodajte osobe da započnete razgovor" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Nije dostupno" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Blutut uređaj na %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "Otisak %s:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Zahtev za uparivanje od %s" #: src/service/device.js:800 msgid "Reject" msgstr "Odbij" #: src/service/device.js:805 msgid "Accept" msgstr "Prihvati" #: src/service/manager.js:114 msgid "" "Discovery has been disabled due to the number of devices on this network." msgstr "Otkrivanje je onemogućeno usled broja uređaja u ovoj mreži." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "" #: src/service/backends/lan.js:452 #, fuzzy msgid "Port already in use" msgstr "%s je spreman za upotrebu" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, fuzzy, javascript-format msgid "%s: Battery is full" msgstr "%s: baterija je pri kraju" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "Potpuno puna" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, fuzzy, javascript-format msgid "%d%% Charged" msgstr "Potpuno puna" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: baterija je pri kraju" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% preostaje" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Ostava" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "Slanje u ostavu" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "Dovlačenje iz ostave" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Nađi mi telefon" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "Podloga za miÅ”a" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Tastatura" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 #, fuzzy msgid "Unknown" msgstr "Nepoznat kontakt" #: src/service/plugins/notification.js:16 #, fuzzy msgid "Share notifications with the paired device" msgstr "PoÅ”alji na mobilni uređaj" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Otkaži obaveÅ”tenje" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Zatvori obaveÅ”tenje" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "ObaveÅ”tenje o oddgovoru" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Aktiviraj obaveÅ”tenje" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Prenos nije uspeo" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Neuspelo slanje ā€ž%sā€œ na %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 #, fuzzy msgid "Presentation" msgstr "Ugrađivanje u Fajlove" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "IzvrÅ”avanje narebi" #: src/service/plugins/runcommand.js:13 msgid "" "Run commands on your paired device or let the device run predefined commands " "on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Montiraj" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Demontiraj" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Deljenje" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 #, fuzzy msgid "Transferring File" msgstr "Prenos nije uspeo" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "Primam ā€ž%sā€œ od %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "UspeÅ”an prenos" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "Primih ā€ž%sā€œ od %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Otvori fasciklu" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Otvori fajl" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "NeuspeÅ”an prijem ā€ž%sā€œ od %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Deljeni tekst od %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Slanje ā€ž%sā€œ za %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "Poslah ā€ž%sā€œ za %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "PoÅ”alji fajlove na %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 #, fuzzy msgid "Open when done" msgstr "Otvori u pregledaču" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "PoÅ”alji veze na %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Novi SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "Odgovori na SMS" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "Deli SMS" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Sistemska jačina" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 #, fuzzy msgid "PulseAudio not found" msgstr "GreÅ”ka Pulsaudija" #: src/service/plugins/telephony.js:14 msgid "" "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "UtiÅ”aj poziv" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Nepoznat kontakt" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Dolazni poziv" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Tekući poziv" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 #, fuzzy msgid "Work" msgstr "%s惻Posao" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 #, fuzzy msgid "Mobile" msgstr "%s惻Mobilni" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 #, fuzzy msgid "Home" msgstr "%s惻Kućni" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "PoÅ”alji na %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Upravo sada" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Juče惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minut" msgstr[1] "%d minuta" msgstr[2] "%d minuta" #: src/service/ui/messaging.js:750 #, fuzzy msgid "Group Message" msgstr "Nova poruka" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "" msgstr[1] "" msgstr[2] "" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (Procenjujem…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d∶%02d do pune)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d∶%02d preostaje)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Odgovori" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Deli veze GSKonektom, direktno u pregldač ili putem SMS-a." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Servis nije dostupan" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Otvori u pregledaču" #~ msgid "Add" #~ msgstr "Dodaj" #~ msgid "On" #~ msgstr "Uključen" #~ msgid "Off" #~ msgstr "Isključen" #~ msgid "Set Shortcut" #~ msgstr "Postavi prečicu" #~ msgid "Authentication Failure" #~ msgstr "GreÅ”ka autentifikacje" #~ msgid "Network Error" #~ msgstr "GreÅ”ka mreže" #~ msgid "Keyboard not ready" #~ msgstr "Tastatura nije spremna" #~ msgid "All files" #~ msgstr "Svi fajlovi" #~ msgid "Camera pictures" #~ msgstr "Slike sa kamere" #, fuzzy, javascript-format #~ msgid "%s惻Other" #~ msgstr "%s惻Drugo" #, javascript-format #~ msgid "%s惻Fax" #~ msgstr "%s惻Faks" #, javascript-format #~ msgid "%d hour" #~ msgid_plural "%d hours" #~ msgstr[0] "%d čas" #~ msgstr[1] "%d časa" #~ msgstr[2] "%d časova" #, javascript-format #~ msgid "Until %s (%s)" #~ msgstr "Do %s (%s)" #~ msgid "Do Not Disturb" #~ msgstr "Ne uznemiravaj" #~ msgid "Until you turn off Do Not Disturb" #~ msgstr "Dok ne isključim Ne uznemiravaj" #~ msgid "Done" #~ msgstr "Gotovo" #~ msgid "Silence Mobile Device Notifications" #~ msgstr "UtiÅ”aj obaveÅ”tenja mobilinih uređaja" #~ msgid "Command Shortcuts" #~ msgstr "Prečice naredbi" #~ msgid "Delete" #~ msgstr "ObriÅ”i" #~ msgid "Delete this device" #~ msgstr "ObriÅ”i ovaj uređaj" #~ msgid "Unpair and remove all settings and files" #~ msgstr "Raspari i ukloni sve postavke i fajlove" #~ msgid "Debugger" #~ msgstr "Ispravljač greÅ”aka" #~ msgid "About" #~ msgstr "O programu" #, fuzzy #~ msgid "Switch to Bluetooth" #~ msgstr "Poveži Blututom" #~ msgid "Appearance" #~ msgstr "Izgled" #~ msgid "Discoverable" #~ msgstr "Otkrivanje je omogućeno" #~ msgid "Restart Service" #~ msgstr "Ponovo pokreni servis" #~ msgid "Settings" #~ msgstr "PodeÅ”avanje" #~ msgid "Remote Filesystems" #~ msgstr "Udaljeni sistemi fajlova" #~ msgid "Sound Effects" #~ msgstr "Zvučni efekti" #~ msgid "Extended Keyboard Support" #~ msgstr "ProÅ”irena podrÅ”ka za tastaturu" #~ msgid "Desktop Contacts" #~ msgstr "Kontakti sa desktopa" #~ msgid "Additional Features" #~ msgstr "Dodatne mogućnosti" # Leaving the name in original form for About dialog. #~ msgid "KDE Connect" #~ msgstr "KDE Connect" #~ msgid "Click to open preferences" #~ msgstr "Kliknite da otvorite postavke" #~ msgid "Additional Software Required" #~ msgstr "Neophodan je dodatan softver" #~ msgid "%s Plugin Failed To Load" #~ msgstr "Neuspelo učitavanje prključka %s" #~ msgid "Wayland Not Supported" #~ msgstr "Vejland nije podržan" #~ msgid "Remote input not supported on Wayland" #~ msgstr "Unos na daljinu nije podržan pod Vejlandom" # Leaving the name in original form for About dialog. #~ msgid "GSConnect: %s" #~ msgstr "GSConnect: %s" #~ msgid "Reconnect" #~ msgstr "Ponovo poveži" #~ msgid "Locate Device" #~ msgstr "Lociranje uređaja" #~ msgid "%s asked to locate this device" #~ msgstr "%s traži ovaj uređaj" #~ msgid "Found" #~ msgstr "Nađen" #~ msgid "Starting Transfer" #~ msgstr "Započinjem prenos" gnome-shell-extension-gsconnect-50/po/sv-SE.po000066400000000000000000001042461421543444100214310ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-12-08 20:22\n" "Last-Translator: \n" "Language-Team: Swedish\n" "Language: sv_SE\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: sv-SE\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "Implementering av KDE Connect fƶr GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "GSConnect-gruppen" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnect Ƥr en komplett implementation av KDE Connect speciellt fƶr GNOME Shell med Nautilus-, Chrome- och Firefox-integration. KDE Connect-gruppen har applikationer fƶr Linux, BSD, Android, Sailfish, macOS och Windows." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "Med GSConnect kan du sƤkert ansluta till mobila enheter och andra skrivbord till:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Dela filer, lƤnkar och text" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Skicka och ta emot meddelanden" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Synkronisera urklippets innehĆ„ll" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Synkronisera kontakter" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Synkronisera aviseringar" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Styr mediaspelare" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "Styr systemets volym" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Kƶr fƶrdefinierade kommandon" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "Och mer…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect i GNOME Shell" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Anslut till…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Avbryt" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Anslut" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "Ip-adress" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "Inga kontakter" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "HjƤlp" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Skriv in ett telefonnummer eller namn" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Ɩvrigt" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "Skicka SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Skicka" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Enheten Ƥr frĆ„nkopplad" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Skicka meddelande" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Skriv ett meddelande" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "Meddelandepost" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Skriv ett meddelande och tryck pĆ„ Enter fƶr att skicka" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Meddelanden" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Ny konversation" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Inga konversationer" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Ingen konversation vald" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "VƤlj eller starta en konversation" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Redigera kommando" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Spara" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "Namn" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Kommandorad" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "VƤlj en kƶrbar fil" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "Ɩppna" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Skrivbord" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "Kamera" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Urklippssynkronisering" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Mediaspelare" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Mus & tangentbord" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Volymkontroll" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Filer" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Ta emot filer" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Spara filer till" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Delning" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Batteri fƶr enhet" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Avisering vid lĆ„g batterinivĆ„" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "Avisering nƤr laddning uppgĆ„r till instƤlld nivĆ„" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Avisering nƤr fulladdad" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Systemets batteri" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Dela statistik" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Batteri" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Kommandon" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "LƤgg till kommando" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Dela aviseringar" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Dela nƤr aktiv" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Program" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Aviseringar" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Kontakter" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Inkommande samtal" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Volym" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Pausa media" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "PĆ„gĆ„ende samtal" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "StƤng av mikrofonen" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Telefon" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "GenvƤgar fƶr Ć„tgƤrd" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "ƅterstƤll alla…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "GenvƤgar" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Insticksmoduler" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Experimentellt" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Enhetscache" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Rensa cache…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Ƅldre SMS-stƶd" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "SFTP-automontering" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "Avancerat" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "TangentbordsgenvƤgar" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "EnhetsinstƤllningar" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Para ihop" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "Enheten Ƥr oparad" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Du kan konfigurera den hƤr enheten innan parning" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Krypteringsinformation" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Ta bort ihopparning" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Till enhet" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "FrĆ„n enhet" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "Inget" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "ƅterstƤll" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "SƤnk" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "StƤng av ljud" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Ange" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Tryck pĆ„ Esc fƶr att avbryta eller Backspace fƶr att Ć„terstƤlla tangentbordsgenvƤgen." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Enhetsnamn" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_Ƅndra namn" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Uppdatera" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "MobilinstƤllningar" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Servicemeny" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "Enhetsmeny" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Redigera enhetsnamn" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Enheter" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Sƶker efter enheter…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "WebblƤsartillƤgg" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Aktivera" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Den hƤr enheten Ƥr osynlig fƶr oparade enheter" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "UpptƤckt inaktiverat" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "VisningslƤge" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Panel" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "AnvƤndarmeny" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Skapa supportlogg" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "Om GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "VƤlj en enhet" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "VƤlj" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Ingen enhet hittades" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Enhetslista" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Rapport" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "NĆ„got gick fel" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "GSConnect stƶtte pĆ„ ett ovƤntat fel. Rapportera problemet och inkludera all information som kan hjƤlpa." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Teknisk information" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "Skicka till mobil enhet" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Mobila enheter" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Aktivera" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d ansluten" msgstr[1] "%d anslutna" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "StƤng av" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Redigera" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Ta bort" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Inaktiverad" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Ange en ny genvƤg fƶr att Ƥndra %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s anvƤnds redan" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "En fullstƤndig implementation av KDE Connect fƶr GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "Morgan Antonsson " #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "Felsƶkningsmeddelanden loggas. Utfƶr de Ć„tgƤrder som krƤvs fƶr att Ć„terskapa problemet och granska sedan loggen." #: src/preferences/service.js:414 msgid "Review Log" msgstr "Granska logg" #: src/preferences/service.js:482 msgid "Laptop" msgstr "BƤrbar dator" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Smartphone" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Surfplatta" #: src/preferences/service.js:488 msgid "Television" msgstr "TV" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Oparad" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Bortkopplad" #: src/preferences/service.js:518 msgid "Connected" msgstr "Ansluten" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "VƤntar pĆ„ tjƤnst…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "Klicka fƶr att fĆ„ hjƤlp med felsƶkning" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "Klicka hƤr fƶr mer information" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "SlĆ„ nummer" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Dela fil" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Visa tillgƤngliga enheter" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Visa alla enheter" #: src/service/daemon.js:369 msgid "Target Device" msgstr "MĆ„lenhet" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Meddelande-text" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Skicka avisering" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "Namn pĆ„ aviseringsapp" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Aviseringstext" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Aviseringsikon" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "Aviserings-ID" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Foto" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Ring" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Dela lƤnk" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Dela text" #: src/service/daemon.js:528 msgid "Show release version" msgstr "Visa utgĆ„vans version" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Inte tillgƤnglig" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Bluetooth-enhet pĆ„ %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "%s fingeravtryck:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "ParfƶrfrĆ„gan frĆ„n %s" #: src/service/device.js:800 msgid "Reject" msgstr "Avvisa" #: src/service/device.js:805 msgid "Accept" msgstr "Acceptera" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "UpptƤckt har inaktiverats pĆ„ grund av antalet enheter pĆ„ detta nƤtverk." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSL kunde inte hittas" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "Porten anvƤnds redan" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "Utbyt batteriinformation" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: Batteriet Ƥr fullt" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "Fulladdad" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "%s: Batteriet har nĆ„tt anpassad laddningsnivĆ„" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "%d%% laddat" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: LĆ„g batterinivĆ„" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% kvar" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Urklipp" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "Dela urklippets innehĆ„ll" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "Skicka urklipp" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "HƤmta urklipp" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "FĆ„ tillgĆ„ng till kontakter frĆ„n den parkopplade enheten" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Hitta min telefon" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "Ring din parkopplade enhet" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "Musmatta" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "Gƶr det mƶjligt fƶr den parkopplade enheten att fungera som en fjƤrrmus och tangentbord" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Tangentbord" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "Dubbelriktad fjƤrrstyrning fƶr medieuppspelning" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "OkƤnd" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "Dela aviseringar med den parkopplade enheten" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Avbryt avisering" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "StƤng avisering" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "Svara pĆ„ avisering" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Aktivera avisering" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "Be den parkopplade enheten att ta ett foto och ƶverfƶra det till den hƤr datorn" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Ɩverfƶring misslyckades" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Det gick inte att skicka ā€%sā€ till %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "Skicka och ta emot pingar" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "Presentation" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "AnvƤnd den parkopplade enheten som presentatƶr" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Kƶr kommandon" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "Kƶr kommandon pĆ„ din parkopplade enhet eller lĆ„t enheten kƶra fƶrdefinierade kommandon pĆ„ den hƤr datorn" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "BlƤddra i den parkopplade enhetens filsystem" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Montera" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Avmontera" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s rapporterade ett felmeddelande" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Dela" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "Dela filer och webbadresser mellan enheter" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s fĆ„r inte ladda upp filer" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "Ɩverfƶr fil" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "Tar emot ā€%sā€ frĆ„n %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Ɩverfƶringen klar" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "Tog emot ā€%sā€ frĆ„n %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Ɩppna mapp" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Ɩppna fil" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Det gick inte att ta emot ā€%sā€ frĆ„n %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Text delad av %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Skickar ā€%sā€ till %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "Skickade ā€%sā€ till %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Skicka filer till %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Ɩppna nƤr det Ƥr klart" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Skicka en lƤnk till %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "Skicka och lƤsa SMS frĆ„n den parade enheten och bli meddelad om nya SMS" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Nytt SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "Svara SMS" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "Dela SMS" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Systemvolym" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "Aktivera den parkopplade enheten fƶr att styra systemets volym" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio kunde inte hittas" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "FĆ„ meddelande om samtal och justera systemets volym vid ringande/pĆ„gĆ„ende samtal" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Tysta samtal" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "OkƤnd kontakt" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Inkommande samtal" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "PĆ„gĆ„ende samtal" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Fax" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Arbete" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Mobil" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Hem" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Skicka till %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Precis nu" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "IgĆ„r惻%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d minut" msgstr[1] "%d minuter" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Gruppmeddelande" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Du: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "Och %d annan kontakt" msgstr[1] "Och %d andra" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "FjƤrrtangentbordet pĆ„ %s Ƥr inte aktivt" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (berƤknar…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d:%02d tills fullt)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d:%02d kvar)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Svara" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "Dela lƤnkar med GSConnect, direkt till webblƤsaren eller med SMS." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "TjƤnsten Ƥr inte tillgƤnglig" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Ɩppna i webblƤsare" gnome-shell-extension-gsconnect-50/po/sv.po000066400000000000000000000717021421543444100211240ustar00rootroot00000000000000# Swedish translations for org.gnome.Shell.Extensions.GSConnect package. # Copyright (C) 2021 THE org.gnome.Shell.Extensions.GSConnect'S COPYRIGHT HOLDER # This file is distributed under the same license as the org.gnome.Shell.Extensions.GSConnect package. # Automatically generated, 2021. # msgid "" msgstr "" "Project-Id-Version: org.gnome.Shell.Extensions.GSConnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-12-08 10:58-0800\n" "PO-Revision-Date: 2021-12-08 10:58-0800\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "" "GSConnect is a complete implementation of KDE Connect especially for GNOME " "Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team " "has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "" "With GSConnect you can securely connect to mobile devices and other desktops " "to:" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:105 msgid "GSConnect in GNOME Shell" msgstr "" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "" #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "" #: data/ui/service-error-dialog.ui:84 msgid "" "GSConnect encountered an unexpected error. Please report the problem and " "include any information that may help." msgstr "" #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:166 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "" msgstr[1] "" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "" #: src/preferences/service.js:411 msgid "" "Debug messages are being logged. Take any steps necessary to reproduce a " "problem then review the log." msgstr "" #: src/preferences/service.js:414 msgid "Review Log" msgstr "" #: src/preferences/service.js:482 msgid "Laptop" msgstr "" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "" #: src/preferences/service.js:486 msgid "Tablet" msgstr "" #: src/preferences/service.js:488 msgid "Television" msgstr "" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "" #: src/preferences/service.js:518 msgid "Connected" msgstr "" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "" #: src/service/daemon.js:351 msgid "List available devices" msgstr "" #: src/service/daemon.js:360 msgid "List all devices" msgstr "" #: src/service/daemon.js:369 msgid "Target Device" msgstr "" #: src/service/daemon.js:411 msgid "Message Body" msgstr "" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "" #: src/service/daemon.js:528 msgid "Show release version" msgstr "" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "" #: src/service/device.js:800 msgid "Reject" msgstr "" #: src/service/device.js:805 msgid "Accept" msgstr "" #: src/service/manager.js:114 msgid "" "Discovery has been disabled due to the number of devices on this network." msgstr "" #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "" #: src/service/plugins/runcommand.js:13 msgid "" "Run commands on your paired device or let the device run predefined commands " "on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "" #: src/service/plugins/telephony.js:14 msgid "" "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "" msgstr[1] "" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "" msgstr[1] "" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "" #: src/shell/notification.js:54 msgid "Reply" msgstr "" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "" #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "" gnome-shell-extension-gsconnect-50/po/tr.po000066400000000000000000001026241421543444100211170ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-09 01:50\n" "Last-Translator: \n" "Language-Team: Turkish\n" "Language: tr_TR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: tr\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "GNOME iƧin KDE Connect uygulaması" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "GSConnect Takımı" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnect, ƶzellikle Nautilus, Chrome ve Firefox bütünleşmeli GNOME Shell iƧin eksiksiz bir KDE Connect uygulamasıdır. KDE Connect ekibinin Linux, BSD, Android, Sailfish, macOS ve Windows iƧin uygulamaları vardır." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "GSConnect ile mobil cihazlara ve diğer masaüstlerine bağlanarak şunları yapabilirsiniz:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Dosyaları, bağlantıları ve metni paylaş" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "Mesaj gƶnder ve al" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Pano iƧeriğini eşitle" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Kişileri eşitle" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Bildirimleri eşitle" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "Medya oynatıcıları kontrol et" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "Sistem sesini kontrol et" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Ɩn tanımlı komutları yürüt" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "Ve daha fazlası…" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GNOME Kabuğunda GSConnect" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "Bağlan…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "İptal" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "Bağlan" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP Adresi" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "Kişi yok" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Yardım" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Telefon numarası veya isim yaz" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Diğer" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "SMS Gƶnder" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "Gƶnder" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "Cihaz Ƨevrim dışı" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "Mesaj Gƶnder" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Bir Mesaj Yaz" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "Mesaj Girdisi" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Bir mesaj yazın ve gƶndermek iƧin Enter tuşuna basın" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "Mesajlaşma" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "Yeni Sohbet" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "Sohbet yok" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "HiƧbir sohbet seƧilmedi" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "Sohbet seƧ ya da başlat" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Komutu Düzenle" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Kaydet" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "İsim" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "Komut Satırı" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "Yürütülebilir dosya seƧ" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "AƧ" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "Masaüstü" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "Kamera" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Pano Eşleşmesi" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "Medya Oynatıcılar" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Fare & Klavye" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "Ses Kontrolü" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Dosyalar" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "Alınan Dosyalar" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Dosyaları şuraya kaydet" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "Paylaşım" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "Cihaz Bataryası" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Düşük Batarya Bildirimi" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Tam Şarj Bildirimi" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Sistem Bataryası" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "İstatistikleri Paylaş" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "Batarya" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "Komutlar" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "Komut Ekle" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Bildirimleri Paylaş" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Aktif Olduğunda Paylaş" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "Uygulamalar" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Bildirimler" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "Kişiler" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "Gelen Ƈağrılar" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Ses" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "Medyayı Duraklat" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "Devam Eden Ƈağrılar" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Mikrofonu Sustur" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Telefon" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Kısayol Eylemleri" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Tümünü Sıfırla…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Kısayollar" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "Eklentiler" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Deneysel" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Cihaz Ɩnbelleği" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "Ɩnbelleği Temizle…" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Eski SMS Desteği" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "SFTP Otomatik Bağla" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "Gelişmiş" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "Klavye Kısayolları" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "Cihaz Ayarları" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Eşleştir" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "Cihaz eşleşmemiş" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Eşleşmeden ƶnce cihazı yapılandır" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Şifreleme Bilgisi" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "Eşleştirmeyi Bitir" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "Cihaza" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Cihazdan" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "HiƧ Biri" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "Geri yükle" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Düşük" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Sessiz" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Ayarla" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "Klavye kısayolunu sıfırlamak iƧin iptal, geri almak iƧin ESC tuşuna bas." #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "Cihaz Adı" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_Adlandır" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "Yenile" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "Mobil Ayarları" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Servis Menüsü" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "Cihaz Menüsü" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Cihaz Adını Düzenle" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "Cihazlar" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Cihazlar aranıyor…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Tarayıcı Eklentileri" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Etkin" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Bu cihaz eşleştirme yapılmayan cihazlara gƶrünmez" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Keşif Devre Dışı" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Gƶrünüm Kipi" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Sistem Paneli" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "Kullanıcı Menüsü" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Destek Günlüğü Oluştur" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "GSConnect Hakkında" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "Cihaz SeƧ" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "SeƧ" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "Cihaz Bulunamadı" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "Cihaz Listesi" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "Rapor" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Bir şeyler ters gitti" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "GSConnect beklenmedik bir hatayla karşılaştı. Lütfen sorunu bildirin ve yardımcı olabilecek tüm bilgileri ekleyin." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Teknik Ayrıntılar" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "Mobil Cihaza Gƶnder" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "Mobil Cihazlar" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "AƧ" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d Bağlı" msgstr[1] "%d Bağlı" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Kapat" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Düzenle" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "Kaldır" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Devre dışı" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Değiştirmek iƧin yeni bir kısayol gir %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s zaten kullanılıyor" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "GNOME iƧin eksiksiz bir KDE Connect uygulaması" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "Serdar Sağlam \n" "Orhan Engin Okay \n" "A. Burak Tektaş " #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "Hata ayıklama mesajları kaydediliyor. Sorunu yeniden oluşturmak iƧin gerekli adımları izleyin ve günlüğü inceleyin." #: src/preferences/service.js:414 msgid "Review Log" msgstr "Günlüğü Gƶzden GeƧir" #: src/preferences/service.js:482 msgid "Laptop" msgstr "Dizüstü" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Telefon" #: src/preferences/service.js:486 msgid "Tablet" msgstr "Tablet" #: src/preferences/service.js:488 msgid "Television" msgstr "Televizyon" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "Eşleşmemiş" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Bağlantı kesildi" #: src/preferences/service.js:518 msgid "Connected" msgstr "Bağlı" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "Hizmet bekleniyor…" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "Sorun giderme konusunda yardım iƧin tıkla" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "Daha fazla bilgi iƧin tıkla" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "Numarayı Ƈevir" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "Dosya Paylaşımı" #: src/service/daemon.js:351 msgid "List available devices" msgstr "Kullanılabilir cihazları listele" #: src/service/daemon.js:360 msgid "List all devices" msgstr "Tüm cihazları listele" #: src/service/daemon.js:369 msgid "Target Device" msgstr "Hedef Cihaz" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Mesaj Metni" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "Bildirim Gƶnder" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "Bildirim Uygulama Adı" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Bildirim Metni" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "Bildirim Simgesi" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "Bildirim Kimliği" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Fotoğraf" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Yokla" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Ƈaldır" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "Bağlantıyı Paylaş" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "Metni Paylaş" #: src/service/daemon.js:528 msgid "Show release version" msgstr "Sürüm versiyonunu gƶster" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Müsait Değil" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Bluetooth cihazı %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "%s Parmak İzi:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "%s tarafından Gelen Eşleşme İsteği" #: src/service/device.js:800 msgid "Reject" msgstr "Reddet" #: src/service/device.js:805 msgid "Accept" msgstr "Kabul et" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "Bu ağdaki cihazların sayısı nedeniyle keşif devre dışı bırakıldı." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSL bulunamadı" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "Bağlantı noktası zaten kullanılıyor" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: Batarya dolu" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "Şarj Oldu" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: Düşük batarya" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "%d%% kaldı" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Pano" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "Panoya Gƶnder" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "Panodan Al" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Telefonumu Bul" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "Fare Desteği" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "Klavye" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "Bilinmeyen" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Bildirimi İptal Et" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Bildirimi Kapat" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "Bildirimi Yanıtla" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "Bildirimi Etkinleştir" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Aktarım Başarısız" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "Gƶnderilemedi ā€œ%sā€ hedef %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Yokla: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "Sunum" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Komutları Ƈalıştır" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Bağla" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Ayır" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s bir hata bildirdi" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "Paylaş" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s dosya yükleme iznine sahip değil" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "Dosya Aktarımı" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "Alınıyor ā€œ%sā€ kaynak %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "Aktarım Başarılı" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "Alınan ā€œ%sā€ kaynak %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "Klasƶrü AƧ" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "Dosyayı AƧ" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "Alım başarısız ā€œ%sā€ kaynak %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Paylaşılan Metin %s" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Gƶnderiliyor ā€œ%sā€ hedef %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "Gƶnderildi ā€œ%sā€ hedef %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "Dosyaları gƶnder %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "Tamamlandığında aƧ" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "Bağlantı gƶnder %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "Yeni SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "SMS Yanıtla" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "SMS Paylaş" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Sistem Sesi" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio bulunamadı" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "Sesi Kapat" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "Bilinmeyen Kişi" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "Gelen Ƨağrı" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "Devam eden Ƨağrı" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Faks" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "İş" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "Mobil" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Ev" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "Gƶnder %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Hemen şimdi" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Dün %s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d dakika" msgstr[1] "%d dakika" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Grup Mesajı" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Sen: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "Ve %d başka kişi" msgstr[1] "Ve %d başkaları" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "%s üzerindeki uzak klavye etkin değil" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (Tahmini…)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (Dolma Süresi %d∶%02d)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (Kalan Süre %d∶%02d)" #: src/shell/notification.js:54 msgid "Reply" msgstr "Yanıtla" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "GSConnect ile bağlantıları doğrudan tarayıcı veya SMS ile paylaş." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Servis Mevcut Değil" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "Tarayıcıda AƧ" gnome-shell-extension-gsconnect-50/po/uk.po000066400000000000000000001170761421543444100211200ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-26 01:18\n" "Last-Translator: \n" "Language-Team: Ukrainian\n" "Language: uk_UA\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: uk\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "Š ŠµŠ°Š»Ń–Š·Š°Ń†Ń–Ń KDE Connect Š“Š»Ń GNOME" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "КоманГа GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnect — це повна Ń€ŠµŠ°Š»Ń–Š·Š°Ń†Ń–Ń KDE Connect ŃŠæŠµŃ†Ń–Š°Š»ŃŒŠ½Š¾ Š“Š»Ń GNOME Shell Š· Ń–Š½Ń‚ŠµŠ³Ń€Š°Ń†Ń–Ń”ŃŽ Š· Nautilus, Chrome і Firefox. КоманГа KDE Connect має ГоГатки Š“Š»Ń Linux, BSD, Android, Sailfish, macOS і Windows." #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "За Š“Š¾ŠæŠ¾Š¼Š¾Š³Š¾ŃŽ GSConnect можна безпечно Š·'Ń”Š“Š½ŃƒŠ²Š°Ń‚ŠøŃŃ Š· Š¼Š¾Š±Ń–Š»ŃŒŠ½ŠøŠ¼Šø ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŠ¼Šø та Ń–Š½ŃˆŠøŠ¼Šø комп'ŃŽŃ‚ŠµŃ€Š°Š¼Šø, щоб:" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "Š”Ń–Š»ŠøŃ‚ŠøŃŃ файлами, ŠæŠ¾ŃŠøŠ»Š°Š½Š½ŃŠ¼Šø і текстом" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "ŠŠ°Š“ŃŠøŠ»Š°Ń‚Šø й Š¾Ń‚Ń€ŠøŠ¼ŃƒŠ²Š°Ń‚Šø ŠæŠ¾Š²Ń–Š“Š¾Š¼Š»ŠµŠ½Š½Ń" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "Š”ŠøŠ½Ń…Ń€Š¾Š½Ń–Š·ŃƒŠ²Š°Ń‚Šø вміст Š±ŃƒŃ„ŠµŃ€Š° Š¾Š±Š¼Ń–Š½Ńƒ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "Š”ŠøŠ½Ń…Ń€Š¾Š½Ń–Š·ŃƒŠ²Š°Ń‚Šø контакти" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "Š”ŠøŠ½Ń…Ń€Š¾Š½Ń–Š·ŃƒŠ²Š°Ń‚Šø ŃŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½Ń" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "ŠšŠµŃ€ŃƒŠ²Š°Ń‚Šø меГіа плеєрами" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "ŠšŠµŃ€ŃƒŠ²Š°Ń‚Šø Š³ŃƒŃ‡Š½Ń–ŃŃ‚ŃŽ системи" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "Š’ŠøŠŗŠ¾Š½ŃƒŠ²Š°Ń‚Šø ŠæŠ¾ŠæŠµŃ€ŠµŠ“Š½ŃŒŠ¾ визначені команГи" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "Та Ń–Š½ŃˆŠµā€¦" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GSConnect у GNOME Shell" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "ŠŸŃ–Š“'Ń”Š“Š½Š°Ń‚ŠøŃŃ Го…" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "Š”ŠŗŠ°ŃŃƒŠ²Š°Ń‚Šø" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "ŠŸŃ–Š“'Ń”Š“Š½Š°Ń‚ŠøŃŃŒ" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP-аГреса" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "ŠŠµŠ¼Š°Ń” контактів" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "Допомога" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "Š’Š²ŠµŠ“Ń–Ń‚ŃŒ номер Ń‚ŠµŠ»ŠµŃ„Š¾Š½Ńƒ або ім'я" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "Š†Š½ŃˆŠøŠ¹" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "ВіГправити SMS" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "ВіГправити" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "ŠŸŃ€ŠøŃŃ‚Ń€Ń–Š¹ віГ'єГнаний" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "ВіГправити ŠæŠ¾Š²Ń–Š“Š¾Š¼Š»ŠµŠ½Š½Ń" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "Š’Š²ŠµŠ“Ń–Ń‚ŃŒ ŠæŠ¾Š²Ń–Š“Š¾Š¼Š»ŠµŠ½Š½Ń" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "Поле ŠæŠ¾Š²Ń–Š“Š¾Š¼Š»ŠµŠ½Š½Ń" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "Š’Š²ŠµŠ“Ń–Ń‚ŃŒ ŠæŠ¾Š²Ń–Š“Š¾Š¼Š»ŠµŠ½Š½Ń та Š½Š°Ń‚ŠøŃŠ½Ń–Ń‚ŃŒ Enter, щоб наГіслати" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "ŠŸŠ¾Š²Ń–Š“Š¾Š¼Š»ŠµŠ½Š½Ń" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "ŠŠ¾Š²Š° бесіГа" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "ŠŠµŠ¼Š°Ń” бесіГ" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "Š–Š¾Š“Š½Ńƒ Š±ŠµŃŃ–Š“Ńƒ не обрано" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "ŠžŠ±ŠµŃ€Ń–Ń‚ŃŒ або Ń€Š¾Š·ŠæŠ¾Ń‡Š½Ń–Ń‚ŃŒ Š±ŠµŃŃ–Š“Ńƒ" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "Š ŠµŠ“Š°Š³ŃƒŠ²Š°Ń‚Šø команГу" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "Зберегти" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "ŠŠ°Š·Š²Š°" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "КоманГний Ń€ŃŠ“Š¾Šŗ" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "ŠžŠ±Ń€Š°Ń‚Šø виконуваний файл" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "ВіГкрити" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "ŠŠ°ŃŃ‚Ń–Š»ŃŒŠ½ŠøŠ¹ комп'ŃŽŃ‚ŠµŃ€" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "ŠšŠ°Š¼ŠµŃ€Š°" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "Š”ŠøŠ½Ń…Ń€Š¾Š½Ń–Š·Š°Ń†Ń–Ń Š±ŃƒŃ„ŠµŃ€Ńƒ Š¾Š±Š¼Ń–Š½Ńƒ" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "ŠœŠµŠ“Ń–Š°ŠæŃ€Š¾Š³Ń€Š°Š²Š°Ń‡Ń–" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "Миша та ŠŗŠ»Š°Š²Ń–Š°Ń‚ŃƒŃ€Š°" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "ŠšŠµŃ€ŃƒŠ²Š°Š½Š½Ń Š³ŃƒŃ‡Š½Ń–ŃŃ‚ŃŽ" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "Файли" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "ŠžŃ‚Ń€ŠøŠ¼ŃƒŠ²Š°Ń‚Šø файли" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "Зберігати файли Го" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "ŠŠ°Š“Š°Š½Š½Ń Š“Š¾ŃŃ‚ŃƒŠæŃƒ" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "ŠŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚Š¾Ń€ ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŽ" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "Š”ŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½Ń про низький Ń€Ń–Š²ŠµŠ½ŃŒ Š·Š°Ń€ŃŠ“Ńƒ" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "Š”ŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½Ń про Š·Š°Ń€ŃŠ“Š¶Š°Š½Š½Ń Го вказаного Ń€Ń–Š²Š½Ń" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "Š”ŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½Ń про повний Š·Š°Ń€ŃŠ“" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "Дистемний Š°ŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚ор" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "Š”Ń–Š»ŠøŃ‚ŠøŃŃ ŃŃ‚Š°Ń‚ŠøŃŃ‚ŠøŠŗŠ¾ŃŽ" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "ŠŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚Š¾Ń€" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "КоманГи" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "ДоГати команГу" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "Š”Ń–Š»ŠøŃ‚ŠøŃŃ ŃŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½ŃŠ¼Šø" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "Š”Ń–Š»ŠøŃ‚ŠøŃŃ при активності" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "ДоГатки" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "Š”ŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½Ń" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "ŠšŠ¾Š½Ń‚Š°ŠŗŃ‚Šø" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "ВхіГні Гзвінки" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "Š“ŃƒŃ‡Š½Ń–ŃŃ‚ŃŒ" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "ŠŸŃ€ŠøŠ·ŃƒŠæŠøŠ½ŠøŃ‚Šø Š²Ń–Š“Ń‚Š²Š¾Ń€ŠµŠ½Š½Ń" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "ŠŸŃ–Š“ час Гзвінків" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "Š’ŠøŠ¼ŠŗŠ½ŃƒŃ‚Šø мікрофон" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "Š¢ŠµŠ»ŠµŃ„Š¾Š½Ń–Ń" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "Š”ŠŗŠ¾Ń€Š¾Ń‡ŠµŠ½Š½Ń Š“Š»Ń Гій" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "Š”ŠŗŠøŠ½ŃƒŃ‚Šø всі…" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "Š”ŠŗŠ¾Ń€Š¾Ń‡ŠµŠ½Š½Ń" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "ŠŸŠ»Š°Š³Ń–Š½Šø" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "Š•ŠŗŃŠæŠµŃ€ŠøŠ¼ŠµŠ½Ń‚Š°Š»ŃŒŠ½Šµ" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "Кеш ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŽ" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "ŠžŃ‡ŠøŃŃ‚ŠøŃ‚Šø ŠŗŠµŃˆā€¦" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "Застаріла піГтримка SMS" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "ŠŠ²Ń‚Š¾Š¼Š¾Š½Ń‚ŃƒŠ²Š°Š½Š½Ń SFTP" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "ДоГатково" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "ŠšŠ»Š°Š²Ń–Š°Ń‚ŃƒŃ€Š½Ń– ŃŠŗŠ¾Ń€Š¾Ń‡ŠµŠ½Š½Ń" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "ŠŠ°Š»Š°ŃˆŃ‚ŃƒŠ²Š°Š½Š½Ń ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŽ" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "Пов'ŃŠ·Š°Ń‚Šø" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "ŠŸŃ€ŠøŃŃ‚Ń€Ń–Š¹ непов'ŃŠ·Š°Š½ŠøŠ¹" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "Š’Šø можете Š½Š°Š»Š°ŃˆŃ‚ŃƒŠ²Š°Ń‚Šø цей пристрій переГ пов'ŃŠ·ŃƒŠ²Š°Š½Š½ŃŠ¼" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "Дані щоГо ŃˆŠøŃ„Ń€ŃƒŠ²Š°Š½Š½Ń" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "ВіГв'ŃŠ·Š°Ń‚Šø" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "До ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŽ" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "Š’Ń–Š“ ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŽ" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "ŠŃ–Ń‡Š¾Š³Š¾" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "ВіГновити" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "Š—Š¼ŠµŠ½ŃˆŠøŃ‚Šø" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "Š’ŠøŠ¼ŠŗŠ½ŃƒŃ‚Šø" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "Встановити" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "ŠŠ°Ń‚ŠøŃŠ½Ń–Ń‚ŃŒ Esc щоб ŃŠŗŠ°ŃŃƒŠ²Š°Ń‚Šø або Backspace щоб ŃŠŗŠøŠ½ŃƒŃ‚Šø ŠŗŠ¾Š¼Š±Ń–Š½Š°Ń†Ń–ŃŽ ŠŗŠ»Š°Š²Ń–Ńˆ" #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "ŠŠ°Š·Š²Š° ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŽ" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "_ŠŸŠµŃ€ŠµŠ¹Š¼ŠµŠ½ŃƒŠ²Š°Ń‚Šø" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "ŠžŠ½Š¾Š²ŠøŃ‚Šø" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "ŠŸŠ°Ń€Š°Š¼ŠµŃ‚Ń€Šø Š¼Š¾Š±Ń–Š»ŃŒŠ½ŠøŃ… пристроїв" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "Дервісне Š¼ŠµŠ½ŃŽ" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "ŠœŠµŠ½ŃŽ ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŽ" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "Š ŠµŠ“Š°Š³ŃƒŠ²Š°Ń‚Šø ім'я ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŽ" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "ŠŸŃ€ŠøŃŃ‚Ń€Š¾Ń—" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "Пошук пристроїв…" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "Š‘Ń€Š°ŃƒŠ·ŠµŃ€Š½Ń– Ń€Š¾Š·ŃˆŠøŃ€ŠµŠ½Š½Ń" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "Š£Š²Ń–Š¼ŠŗŠ½ŃƒŃ‚Šø" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "Цей пристрій є невиГимим Š“Š»Ń непов'ŃŠ·Š°Š½ŠøŃ… пристроїв" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "Š’ŠøŠ“ŠøŠ¼Ń–ŃŃ‚ŃŒ вимкнено" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "Режим Š²Ń–Š“Š¾Š±Ń€Š°Š¶ŠµŠ½Š½Ń" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "Панель" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "ŠœŠµŠ½ŃŽ ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Š°" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "Š—Š³ŠµŠ½ŠµŃ€ŃƒŠ²Š°Ń‚Šø Š¶ŃƒŃ€Š½Š°Š» Š“Š»Ń піГтримки" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "ŠŸŃ€Š¾ GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "ŠžŠ±ŠµŃ€Ń–Ń‚ŃŒ пристрій" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "ŠžŠ±Ń€Š°Ń‚Šø" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "ŠŸŃ€ŠøŃŃ‚Ń€Š¾Ń—Š² не знайГено" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "ŠŸŠµŃ€ŠµŠ»Ń–Šŗ пристроїв" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "ŠŸŠ¾Š²Ń–Š“Š¾Š¼ŠøŃ‚Šø" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "Щось ŠæŃ–ŃˆŠ»Š¾ не так" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "GSConnect ŃŃ‚ŠøŠŗŠ½ŃƒŠ²ŃŃ Š· Š½ŠµŠ¾Ń‡Ń–ŠŗŃƒŠ²Š°Š½Š¾ŃŽ ŠæŠ¾Š¼ŠøŠ»ŠŗŠ¾ŃŽ. Š‘ŃƒŠ“ŃŒ ласка, повіГомте про ŠæŃ€Š¾Š±Š»ŠµŠ¼Ńƒ і ГоГайте буГь-ŃŠŗŃƒ Ń–Š½Ń„Š¾Ń€Š¼Š°Ń†Ń–ŃŽ, ŃŠŗŠ° може Гопомогти." #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "Технічні поГробиці" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "ВіГправити Го Š¼Š¾Š±Ń–Š»ŃŒŠ½Š¾Š³Š¾ ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŽ" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "ŠœŠ¾Š±Ń–Š»ŃŒŠ½Ń– пристрої" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "Š£Š²Ń–Š¼ŠŗŠ½ŃƒŃ‚Šø" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d піГ'єГнано" msgstr[1] "%d піГ'єГнано" msgstr[2] "%d піГ'єГнано" msgstr[3] "%d піГ'єГнано" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "Š’ŠøŠ¼ŠŗŠ½ŃƒŃ‚Šø" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "Š ŠµŠ“Š°Š³ŃƒŠ²Š°Ń‚Šø" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "ВиГалити" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "Вимкнено" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "Š’Š²ŠµŠ“Ń–Ń‚ŃŒ нове ŃŠŗŠ¾Ń€Š¾Ń‡ŠµŠ½Š½Ń, щоб замінити %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s вже Š²ŠøŠŗŠ¾Ń€ŠøŃŃ‚Š¾Š²ŃƒŃ”Ń‚ŃŒŃŃ" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "Повна Ń€ŠµŠ°Š»Ń–Š·Š°Ń†Ń–Ń KDE Connect Š“Š»Ń GNOME" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "kotyhoroshko" #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "ŠŸŠ¾Š²Ń–Š“Š¾Š¼Š»ŠµŠ½Š½Ń Š½Š°Š»Š°Š³Š¾Š“Š¶ŠµŠ½Š½Ń Š·Š°ŠæŠøŃŃƒŃŽŃ‚ŃŒŃŃ. Виконайте всі необхіГні Гії Š“Š»Ń Š²Ń–Š“Ń‚Š²Š¾Ń€ŠµŠ½Š½Ń проблеми, а потім ŠæŠµŃ€ŠµŠ³Š»ŃŠ½ŃŒŃ‚Šµ Š¶ŃƒŃ€Š½Š°Š»." #: src/preferences/service.js:414 msgid "Review Log" msgstr "ŠŸŠµŃ€ŠµŠ³Š»ŃŠ½ŃƒŃ‚Šø Š¶ŃƒŃ€Š½Š°Š»" #: src/preferences/service.js:482 msgid "Laptop" msgstr "ŠŠ¾ŃƒŃ‚Š±ŃƒŠŗ" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "Дмартфон" #: src/preferences/service.js:486 msgid "Tablet" msgstr "ŠŸŠ»Š°Š½ŃˆŠµŃ‚" #: src/preferences/service.js:488 msgid "Television" msgstr "Телевізор" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "ŠŠµ пов'ŃŠ·Š°Š½ŠøŠ¹" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "Š’Ń–Š“'єГнаний" #: src/preferences/service.js:518 msgid "Connected" msgstr "ŠŸŃ–Š“'єГнаний" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "ŠžŃ‡Ń–ŠŗŃƒŠ²Š°Š½Š½Ń ŃŠ»ŃƒŠ¶Š±Šøā€¦" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "ŠŠ°Ń‚ŠøŃŠ½Ń–Ń‚ŃŒ, щоб Гопомогти ŃƒŃŃƒŠ½ŃƒŃ‚Šø ŠæŃ€Š¾Š±Š»ŠµŠ¼Ńƒ" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "ŠŠ°Ń‚ŠøŃŠ½Ń–Ń‚ŃŒ Š“Š»Ń Š¾Ń‚Ń€ŠøŠ¼Š°Š½Š½Ń ГоГаткової інформації" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "ŠŠ°Š±Ń€Š°Ń‚Šø номер" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "ŠŸŠ¾Š“Ń–Š»ŠøŃ‚ŠøŃŃ файлом" #: src/service/daemon.js:351 msgid "List available devices" msgstr "ŠŸŠµŃ€ŠµŠ»Ń–Ń‡ŠøŃ‚Šø Š“Š¾ŃŃ‚ŃƒŠæŠ½Ń– пристрої" #: src/service/daemon.js:360 msgid "List all devices" msgstr "ŠŸŠµŃ€ŠµŠ»Ń–Ń‡ŠøŃ‚Šø всі пристрої" #: src/service/daemon.js:369 msgid "Target Device" msgstr "Š¦Ń–Š»ŃŒŠ¾Š²ŠøŠ¹ пристрій" #: src/service/daemon.js:411 msgid "Message Body" msgstr "Тіло ŠæŠ¾Š²Ń–Š“Š¾Š¼Š»ŠµŠ½Š½Ń" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "ВіГправити ŃŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½Ń" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "ŠŠ°Š·Š²Š° Š·Š°ŃŃ‚Š¾ŃŃƒŠ½ŠŗŃƒ Š“Š»Ń ŃŠæŠ¾Š²Ń–Ń‰ŠµŠ½ŃŒ" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "Тіло ŃŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½Ń" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "ŠŸŃ–ŠŗŃ‚Š¾Š³Ń€Š°Š¼Š° ŃŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½Ń" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "ID ŃŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½Ń" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "Фото" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "ŠŸŃ–Š½Š³" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "Дзвеніти" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "ŠŸŠ¾Š“Ń–Š»ŠøŃ‚ŠøŃŃ ŠæŠ¾ŃŠøŠ»Š°Š½Š½ŃŠ¼" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "ŠŸŠ¾Š“Ń–Š»ŠøŃ‚ŠøŃŃ текстом" #: src/service/daemon.js:528 msgid "Show release version" msgstr "ŠŸŠ¾ŠŗŠ°Š·Š°Ń‚Šø Š²ŠµŃ€ŃŃ–ŃŽ програми" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½ŠøŠ¹" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "Bluetooth-пристрій %s" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "ВіГбиток %s:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "Запит на пов'ŃŠ·ŃƒŠ²Š°Š½Š½Ń віГ %s" #: src/service/device.js:800 msgid "Reject" msgstr "ВіГхилити" #: src/service/device.js:805 msgid "Accept" msgstr "ŠŸŃ€ŠøŠ¹Š½ŃŃ‚Šø" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "Š’ŠøŠ“ŠøŠ¼Ń–ŃŃ‚ŃŒ було вимкнено через велику ŠŗŃ–Š»ŃŒŠŗŃ–ŃŃ‚ŃŒ пристроїв у цій мережі." #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "OpenSSL не знайГено" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "ŠŸŠ¾Ń€Ń‚ уже Š²ŠøŠŗŠ¾Ń€ŠøŃŃ‚Š¾Š²ŃƒŃ”Ń‚ŃŒŃŃ" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "ŠžŠ±Š¼Ń–Š½ Ń–Š½Ń„Š¾Ń€Š¼Š°Ń†Ń–Ń”ŃŽ про Š°ŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚ор" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: ŠŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚Š¾Ń€ Š·Š°Ń€ŃŠ“Š¶ŠµŠ½ŠøŠ¹" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "ŠŸŠ¾Š²Š½Ń–ŃŃ‚ŃŽ Š·Š°Ń€ŃŠ“Š¶ŠµŠ½ŠøŠ¹" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "%s: ŠŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚Š¾Ń€ Š“Š¾ŃŃŠ³ вказаного вами Ń€Ń–Š²Š½Ń Š·Š°Ń€ŃŠ“Ńƒ" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "%d%% Š—Š°Ń€ŃŠ“Š¶ŠµŠ½Š¾" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: ŠŠøŠ·ŃŒŠŗŠøŠ¹ Š·Š°Ń€ŃŠ“ Š°ŠŗŃƒŠ¼ŃƒŠ»ŃŃ‚Š¾Ń€Š°" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "залишилось %d%%" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "Š‘ŃƒŃ„ŠµŃ€ Š¾Š±Š¼Ń–Š½Ńƒ" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "ŠŠ°Š“ŃŠøŠ»Š°Ń‚Šø вміст Š±ŃƒŃ„ŠµŃ€Š° Š¾Š±Š¼Ń–Š½Ńƒ" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "ŠŸŠ¾Š¼Ń–Ń‰Š°Ń‚Šø у Š±ŃƒŃ„ŠµŃ€ Š¾Š±Š¼Ń–Š½Ńƒ" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "ŠžŃ‚Ń€ŠøŠ¼ŃƒŠ²Š°Ń‚Šø Š· Š±ŃƒŃ„ŠµŃ€Ńƒ Š¾Š±Š¼Ń–Š½Ńƒ" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "Š”Š¾ŃŃ‚ŃƒŠæ Го контактів ŠæŠ¾Š²Ź¼ŃŠ·Š°Š½Š¾Š³Š¾ ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŽ" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "Знайти мій телефон" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "Дзвеніти ŠæŠ¾Š²Ź¼ŃŠ·Š°Š½ŠøŠ¼ пристроєм" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "ТачпаГ" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "Š”Š¾Š·Š²Š¾Š»ŃŃ” пов'ŃŠ·Š°Š½Š¾Š¼Ńƒ ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŽ ŠæŃ€Š°Ń†ŃŽŠ²Š°Ń‚Šø Š²Ń–Š“Š“Š°Š»ŠµŠ½Š¾ŃŽ Š¼ŠøŃˆŠµŃŽ та ŠŗŠ»Š°Š²Ń–Š°Ń‚ŃƒŃ€Š¾ŃŽ" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "ŠšŠ»Š°Š²Ń–Š°Ń‚ŃƒŃ€Š°" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "Двостороннє віГГалене ŠŗŠµŃ€ŃƒŠ²Š°Š½Š½Ń Š²Ń–Š“Ń‚Š²Š¾Ń€ŠµŠ½Š½ŃŠ¼ меГіа" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "ŠŠµŠ²Ń–Š“Š¾Š¼Š¾" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "Š”Ń–Š»ŠøŃ‚ŠøŃŃ ŃŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½ŃŠ¼Šø Š· ŠæŠ¾Š²Ź¼ŃŠ·Š°Š½ŠøŠ¼ пристроєм" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "Š”ŠŗŠ°ŃŃƒŠ²Š°Ń‚Šø ŃŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½Ń" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "Закрити ŃŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½Ń" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "ВіГповісти на ŃŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½Ń" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "ŠŠŗŃ‚ŠøŠ²ŃƒŠ²Š°Ń‚Šø ŃŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½Ń" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "Запит пов'ŃŠ·Š°Š½Š¾Š³Š¾ ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŽ зробити фото та переслати його Го Ń†ŃŒŠ¾Š³Š¾ ПК" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "Помилка переГачі" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "ŠŠµ Š²Š“Š°Š»Š¾ŃŃ віГправити Ā«%sĀ» Го %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "ŠŠ°Š“ŃŠøŠ»Š°Š½Š½Ń та Š¾Ń‚Ń€ŠøŠ¼Š°Š½Š½Ń ŠæŃ–Š½Š³Ńƒ" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "ŠŸŃ–Š½Š³: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "ŠŸŃ€ŠµŠ·ŠµŠ½Ń‚Š°Ń†Ń–Ń" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "Š’ŠøŠŗŠ¾Ń€ŠøŃŃ‚Š¾Š²ŃƒŠ¹Ń‚Šµ спарений пристрій презентатором" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "Виконати команГи" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "Š’ŠøŠŗŠ¾Š½ŃƒŠ¹Ń‚Šµ команГи на вашому ŠæŠ¾Š²Ź¼ŃŠ·Š°Š½Š¾Š¼Ńƒ пристрої або Š“Š¾Š·Š²Š¾Š»ŃŒŃ‚Šµ ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŽ Š²ŠøŠŗŠ¾Š½ŃƒŠ²Š°Ń‚Šø ŠæŠ¾ŠæŠµŃ€ŠµŠ“Š½ŃŒŠ¾ визначені команГи на Ń†ŃŒŠ¾Š¼Ńƒ ПК" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "ŠŸŠµŃ€ŠµŠ³Š»ŃŠ“ файлової системи пов'ŃŠ·Š°Š½Š¾Š³Š¾ ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŽ" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "Š—Š¼Š¾Š½Ń‚ŃƒŠ²Š°Ń‚Šø" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "Š”ŠµŠ¼Š¾Š½Ń‚ŃƒŠ²Š°Ń‚Šø" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s ŠæŠ¾Š²Ń–Š“Š¾Š¼Š»ŃŃ” про помилку" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "ŠŸŠ¾Š“Ń–Š»ŠøŃ‚ŠøŃŃŒ" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "Š”Ń–Š»Ń–Ń‚ŃŒŃŃ файлами й URL-аГресами між ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŠ¼Šø" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s не має Гозволу Š²ŠøŠ²Š°Š½Ń‚Š°Š¶ŃƒŠ²Š°Ń‚Šø файли" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "ŠŸŠµŃ€ŠµŠ“Š°Š²Š°Š½Š½Ń Ń„Š°Š¹Š»Ńƒ" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "ŠžŃ‚Ń€ŠøŠ¼Š°Š½Š½Ń Ā«%sĀ» віГ %s" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "ŠŸŠµŃ€ŠµŠ“Š°Ń‡Š° ŃƒŃŠæŃ–ŃˆŠ½Š°" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "ŠžŃ‚Ń€ŠøŠ¼Š°Š½Š¾ Ā«%sĀ» віГ %s" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "ВіГкрити каталог" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "ВіГкрити файл" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "ŠŠµ Š²Š“Š°Š»Š¾ŃŃ отримати Ā«%sĀ» віГ %s" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "Текст " #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "Š’Ń–Š“ŠæŃ€Š°Š²Š»ŠµŠ½Š½Ń Ā«%sĀ» Го %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "Ā«%sĀ» наГіслано Го %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "ВіГправити файли Го %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "ВіГкрити ŠæŃ–ŃŠ»Ń Š·Š°Š²ŠµŃ€ŃˆŠµŠ½Š½Ń" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "ВіГправити ŠæŠ¾ŃŠøŠ»Š°Š½Š½Ń Го %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "SMS" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "ŠŠ°Š“ŃŠøŠ»Š°Š¹Ń‚Šµ і читайте SMS вашого ŠæŠ¾Š²Ź¼ŃŠ·Š°Š½Š¾Š³Š¾ ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŽ та Š¾Ń‚Ń€ŠøŠ¼ŃƒŠ¹Ń‚Šµ ŃŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½Ń про нові SMS" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "ŠŠ¾Š²Šµ SMS (URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "ВіГповісти на SMS" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "ŠŸŠ¾Š“Ń–Š»ŠøŃ‚ŠøŃŃŒ SMS" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "Š“ŃƒŃ‡Š½Ń–ŃŃ‚ŃŒ системи" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "Š£Š²Ń–Š¼ŠŗŠ½Ń–Ń‚ŃŒ ŠŗŠµŃ€ŃƒŠ²Š°Š½Š½Ń ŃŠøŃŃ‚ŠµŠ¼Š½Š¾ŃŽ Š³ŃƒŃ‡Š½Ń–ŃŃ‚ŃŽ ŠæŠ¾Š²Ź¼ŃŠ·Š°Š½Š¾Š³Š¾ ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŽ" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "PulseAudio не знайГено" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "ŠžŃ‚Ń€ŠøŠ¼ŃƒŠ²Š°Ń‚Šø ŃŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½Ń про виклики та Š·Š¼Ń–Š½ŃŽŠ²Š°Ń‚Šø ŃŠøŃŃ‚ŠµŠ¼Š½Ńƒ Š³ŃƒŃ‡Š½Ń–ŃŃ‚ŃŒ піГ час Гзвінка/поточних викликів" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "ŠŸŃ€ŠøŠ³Š»ŃƒŃˆŠøŃ‚Šø Гзвінок" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "ŠŠµŠ²Ń–Š“Š¾Š¼ŠøŠ¹ контакт" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "ВхіГний Гзвінок" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "ŠŸŠ¾Ń‚Š¾Ń‡Š½ŠøŠ¹ виклик" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "Факс" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "Длужбовий" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "ŠœŠ¾Š±Ń–Š»ŃŒŠ½ŠøŠ¹" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "Š”Š¾Š¼Š°ŃˆŠ½Ń–Š¹" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "ВіГправити Го %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "Š¢Ń–Š»ŃŒŠŗŠø що" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "Š£Ń‡Š¾Ń€Š°ćƒ»%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d хвилина" msgstr[1] "%d хвилини" msgstr[2] "%d хвилин" msgstr[3] "%d хвилини" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "Š“Ń€ŃƒŠæŠ¾Š²Šµ ŠæŠ¾Š²Ń–Š“Š¾Š¼Š»ŠµŠ½Š½Ń" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "Š’Šø: %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "І ще %d Ń–Š½ŃˆŠøŠ¹ контакт" msgstr[1] "І ще %d Ń–Š½ŃˆŃ–" msgstr[2] "І ще %d Ń–Š½ŃˆŠøŃ…" msgstr[3] "І ще %d Ń–Š½ŃˆŠøŃ…" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "ВіГГалена ŠŗŠ»Š°Š²Ń–Š°Ń‚ŃƒŃ€Š° на пристрої %s не активна" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (Š Š¾Š·Ń€Š°Ń…Š¾Š²ŃƒŃ”Ń‚ŃŒŃŃā€¦)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d∶%02d Го Š·Š°Ń€ŃŠ“Š¶ŠµŠ½Š½Ń)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d∶%02d Š·Š°Š»ŠøŃˆŠøŠ»Š¾ŃŃ)" #: src/shell/notification.js:54 msgid "Reply" msgstr "ВіГповісти" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "ŠŸŠ¾ŃˆŠøŃ€ŃŽŠ¹Ń‚Šµ ŠæŠ¾ŃŠøŠ»Š°Š½Š½Ń за Š“Š¾ŠæŠ¾Š¼Š¾Š³Š¾ŃŽ GSConnect, Š½Š°ŠæŃ€ŃŠ¼Ńƒ Го Š±Ń€Š°ŃƒŠ·ŠµŃ€Š° або через SMS." #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "Дервіс Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½ŠøŠ¹" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "ВіГкрити у Š±Ń€Š°ŃƒŠ·ŠµŃ€Ń–" gnome-shell-extension-gsconnect-50/po/zh_CN.po000066400000000000000000001011431421543444100214660ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-09 01:50\n" "Last-Translator: \n" "Language-Team: Chinese Simplified\n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: zh-CN\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "KDE Connect ēš„ GNOME å®žēŽ°" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "GSConnect 团队" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "GSConnect ę˜Æäø€äøŖäøŗ GNOME ē¼–å†™ēš„ KDE Connect ēš„å®Œę•“å®žēŽ°ļ¼Œå¹¶äø”åÆä»„å’Œ Nautilus态Chrome 和 Firefox 集ꈐ怂KDE Connect å›¢é˜Ÿęœ‰äøŗ Linux态BSDć€å®‰å“ļ¼ˆAndroid)、Sailfish态macOS 和 Windows č®¾č®”ēš„å®¢ęˆ·ē«Æć€‚" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "ę‚ØåÆä»„ä½æē”Ø GSConnect å®‰å…Øåœ°čæžęŽ„åˆ°ē§»åŠØč®¾å¤‡å’Œå…¶ä»–ę”Œé¢ļ¼Œę„ļ¼š" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "å‘é€ę–‡ä»¶ć€é“¾ęŽ„å’Œę–‡ęœ¬" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "å‘é€ć€ęŽ„ę”¶ę¶ˆęÆ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "åŒę­„å‰Ŗč““ęæå†…å®¹" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "åŒę­„č”ē³»äŗŗ" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "åŒę­„é€šēŸ„" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "ęŽ§åˆ¶åŖ’ä½“ę’­ę”¾å™Ø" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "ęŽ§åˆ¶ē³»ē»ŸéŸ³é‡" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "ę‰§č”Œé¢„å®šä¹‰ēš„å‘½ä»¤" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "ä»„åŠę›“å¤šåŠŸčƒ½ć€‚" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "GNOME Shell äø­ēš„ GSConnect" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "čæžęŽ„åˆ°..." #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "å–ę¶ˆ" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "čæžęŽ„" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP 地址" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "ę²”ęœ‰č”ē³»äŗŗ" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "帮助" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "č¾“å…„ē”µčÆå·ē ęˆ–å§“å" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "其他" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "å‘é€ēŸ­äæ”" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "发送" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "č®¾å¤‡å·²ę–­å¼€čæžęŽ„" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "å‘é€ę¶ˆęÆ" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "č¾“å…„ę¶ˆęÆ" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "ę¶ˆęÆå†…å®¹" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "č¾“å…„ę¶ˆęÆå¹¶ęŒ‰å›žč½¦å‘é€" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "消息" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "ę–°å»ŗåÆ¹čÆ" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "ę²”ęœ‰åÆ¹čÆ" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "ęœŖé€‰ę‹©åÆ¹čÆ" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "é€‰ę‹©ęˆ–å¼€å§‹åÆ¹čÆ" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "编辑命令" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "äæå­˜" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "åē§°" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "å‘½ä»¤č”Œ" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "é€‰ę‹©åÆę‰§č”Œę–‡ä»¶" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "打开" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "ę”Œé¢" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "ē›øęœŗ" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "å‰Ŗč““ęæåŒę­„" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "媒体播放" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "é¼ ę ‡å’Œé”®ē›˜" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "éŸ³é‡ęŽ§åˆ¶" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "ꖇ件" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "ꖇ件ꎄꔶ" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "å°†ę–‡ä»¶äæå­˜åˆ°" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "共享" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "č®¾å¤‡ē”µé‡" #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "ä½Žē”µé‡é€šēŸ„" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "å……ę»”ē”µé€šēŸ„" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "ē³»ē»Ÿē”µę± " #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "åˆ†äŗ«ē»Ÿč®”äæ”ęÆ" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "电池" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "命令" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "ę·»åŠ å‘½ä»¤" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "åŒę­„é€šēŸ„" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "č®¾å¤‡äŗ®čµ·ę—¶ä»åŒę­„é€šēŸ„" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "应用" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "é€šēŸ„" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "č”ē»œ" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "ę„ē”µ" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "音量" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "ęš‚åœåŖ’ä½“" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "ę­£åœØčæ›č”Œēš„å‘¼å«" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "éŗ¦å…‹é£Žé™éŸ³" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "ē”µčÆ" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "ę“ä½œåæ«ę·ę–¹å¼" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "å…ØéƒØé‡ē½®..." #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "åæ«ę·ę–¹å¼" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "ę’ä»¶" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "å®žéŖŒåŠŸčƒ½" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "č®¾å¤‡ē¼“å­˜" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "ęø…é™¤ē¼“å­˜..." #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "ę—§ē‰ˆēŸ­äæ”ę”ÆęŒ" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "č‡ŖåŠØęŒ‚č½½ SFTP" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "高级" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "é”®ē›˜åæ«ę·é”®" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "设备设置" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "é…åÆ¹" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "č®¾å¤‡ęœŖé…åÆ¹" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "ę‚ØåÆä»„åœØé…åÆ¹å‰é…ē½®ę­¤č®¾å¤‡" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "åŠ åÆ†äæ”ęÆ" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "å–ę¶ˆé…åÆ¹" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "åˆ°č®¾å¤‡" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "ä»Žč®¾å¤‡" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "ꗠ" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "ę¢å¤" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "降低" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "静音" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "设置" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "ꌉ Esc é”®å–ę¶ˆļ¼Œęˆ–ęŒ‰ Backsace é”®é‡ē½®é”®ē›˜åæ«ę·ę–¹å¼ć€‚" #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "č®¾å¤‡åē§°" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "äæ®ę”¹åē§°" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "åˆ·ę–°" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "设置" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "ęœåŠ”čœå•" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "č®¾å¤‡čœå•" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "ē¼–č¾‘č®¾å¤‡åē§°" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "设备" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "ę­£åœØęœē“¢č®¾å¤‡ā€¦" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "ęµč§ˆå™Øé™„åŠ ē»„ä»¶" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "启用" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "ę­¤č®¾å¤‡åÆ¹ęœŖé…åÆ¹ēš„č®¾å¤‡äøåÆč§" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "å‘ēŽ°å·²ē¦ē”Ø" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "ę˜¾ē¤ŗęØ”å¼" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "é¢ęæ" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "ē”Øęˆ·čœå•" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "ē”Ÿęˆę”ÆęŒę—„åæ—" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "å…³äŗŽ GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "选择设备" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "选ꋩ" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "ę²”ęœ‰ę‰¾åˆ°č®¾å¤‡" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "č®¾å¤‡åˆ—č”Ø" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "ęŠ„å‘Š" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "出了一些错误" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "GSConnect é‡åˆ°äŗ†ę„å¤–ēš„é”™čÆÆļ¼ŒčÆ·ęŠ„å‘Ščæ™äøŖé”™čÆÆļ¼Œå¹¶åœØęŠ„å‘Šäø­åŒ…å«ä»»ä½•ę‚Øč®¤äøŗåÆčƒ½ęœ‰ē”Øēš„äæ”ęÆć€‚" #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "ęŠ€ęœÆäæ”ęÆ" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "å‘é€åˆ°ē§»åŠØč®¾å¤‡" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "ē§»åŠØč®¾å¤‡" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "开启" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d čæžęŽ„" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "关闭" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "编辑" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "删除" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "禁用" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "č¾“å…„ę–°ēš„åæ«ę·ę–¹å¼ %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "%s å·²åœØä½æē”Øäø­" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "KDE Connect 在 GNOME ēš„å®Œę•“å®žēŽ°" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "ēæ»čÆ‘č“”ēŒ®" #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "ę­£åœØč®°å½•č°ƒčÆ•ę—„åæ—ć€‚čÆ·é‡‡å–ä»»ä½•åæ…č¦ēš„ę­„éŖ¤ę„é‡ēŽ°é—®é¢˜ļ¼Œē„¶åŽå³åÆęŸ„ēœ‹ę—„åæ—ć€‚" #: src/preferences/service.js:414 msgid "Review Log" msgstr "ęŸ„ēœ‹ę—„åæ—" #: src/preferences/service.js:482 msgid "Laptop" msgstr "ē¬”č®°ęœ¬ē”µč„‘" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "ę™ŗčƒ½ę‰‹ęœŗ" #: src/preferences/service.js:486 msgid "Tablet" msgstr "å¹³ęæē”µč„‘" #: src/preferences/service.js:488 msgid "Television" msgstr "电视" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "ęœŖé…åÆ¹" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "已断开" #: src/preferences/service.js:518 msgid "Connected" msgstr "å·²čæžęŽ„" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "ē­‰å¾…ęœåŠ”å“åŗ”..." #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "å•å‡»ä»„čŽ·å–ē–‘éš¾č§£ē­”åø®åŠ©" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "å•å‡»ä»„čŽ·å–čÆ¦ē»†äæ”ęÆ" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "ę‹Øę‰“å·ē " #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "共享文件" #: src/service/daemon.js:351 msgid "List available devices" msgstr "åÆē”Øč®¾å¤‡åˆ—č”Ø" #: src/service/daemon.js:360 msgid "List all devices" msgstr "ę‰€ęœ‰åÆē”Øč®¾å¤‡" #: src/service/daemon.js:369 msgid "Target Device" msgstr "目标设备" #: src/service/daemon.js:411 msgid "Message Body" msgstr "ę¶ˆęÆę­£ę–‡" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "å‘é€é€šēŸ„" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "é€šēŸ„ēš„åŗ”ē”Øåē§°" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "é€šēŸ„ēš„äæ”ęÆę­£ę–‡" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "é€šēŸ„ēš„å›¾ę ‡" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "é€šēŸ„ēš„ID" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "照片" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "响铃" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "å…±äŗ«é“¾ęŽ„" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "å…±äŗ«ę–‡ęœ¬" #: src/service/daemon.js:528 msgid "Show release version" msgstr "ę˜¾ē¤ŗē‰ˆęœ¬äæ”ęÆ" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "ę— ę³•ä½æē”Øēš„" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "ä½äŗŽ %s ēš„č“ē‰™č®¾å¤‡" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "%s ēš„ęŒ‡ēŗ¹:" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "%s čÆ·ę±‚é…åÆ¹" #: src/service/device.js:800 msgid "Reject" msgstr "ę‹’ē»" #: src/service/device.js:805 msgid "Accept" msgstr "ęŽ„å—" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "ē”±äŗŽčæ™äøŖē½‘ē»œäøŠēš„č®¾å¤‡ę•°é‡čæ‡å¤šļ¼Œå‘ēŽ°å·²č¢«ē¦ē”Øć€‚" #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "ę— ę³•ę‰¾åˆ° OpenSSL(OpenSSL not found)" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "ē«Æå£å·²č¢«å ē”Ø" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%s: 电池已充滔" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "充滔电" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%s: ē”µę± ē”µé‡ä½Ž" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "剩余 %d%%" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "å‰Ŗč““ęæ" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "ęŽØé€å‰Ŗč““ęæ" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "čŽ·å–å‰Ŗč““ęæ" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "ęŸ„ę‰¾ęˆ‘ēš„ę‰‹ęœŗ" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "é¼ ę ‡" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "é”®ē›˜" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "未矄" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "å–ę¶ˆé€šēŸ„" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "å…³é—­é€šēŸ„" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "å›žå¤é€šēŸ„" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "ęæ€ę“»é€šēŸ„" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "传输失蓄" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "无法将 ā€œ%sā€ 发送到 %s" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "čæœēØ‹č¾“å…„" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "čæč”Œå‘½ä»¤" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "ęŒ‚č½½" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "åøč½½" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "%s ęŠ„å‘Šäŗ†äø€äøŖé”™čÆÆ" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "共享" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "%s äøå…č®øäøŠä¼ ę–‡ä»¶" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "文件传输中" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "ę­£åœØä»Ž %2$s ꎄꔶ ā€œ%1$sā€" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "ä¼ č¾“ęˆåŠŸ" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "已从 %2$s ꎄꔶ ā€œ%1$sā€" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "打开目录" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "打开文件" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "ę— ę³•ä»Ž %2$s ꎄꔶ ā€œ%1$sā€" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "%s å…±äŗ«ēš„ę–‡ęœ¬" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "ę­£åœØå°† ā€œ%sā€ 发送到 %s" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "已将发送 ā€œ%sā€ 到 %s" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "å‘é€ę–‡ä»¶åˆ° %s" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "å®ŒęˆåŽę‰“å¼€" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "å‘é€é“¾ęŽ„åˆ° %s" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "短俔" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "ę–°ēŸ­äæ”(URI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "å›žå¤ēŸ­äæ”" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "å…±äŗ«ēŸ­äæ”" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "ē³»ē»ŸéŸ³é‡" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "ęœŖę‰¾åˆ° PulseAudio" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "é™éŸ³é€ščÆ" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "ęœŖēŸ„č”ē³»äŗŗ" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "ę„ē”µ" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "å‘¼å«äø­" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "传真" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "单位" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "ę‰‹ęœŗ" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "住宅" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "发送到 %s" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "åˆšę‰" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "昨天・%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d 分钟" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "ē¾¤ē»„ę¶ˆęÆ" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "ä½ : %s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "添加 %d ä½č”ē³»äŗŗ" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "%s äøŠēš„čæœēØ‹é”®ē›˜ęœŖęæ€ę“»" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (ä¼°č®”...)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%% (%d∶%02d äøŗę­¢)" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (剩余 %d∶%02d)" #: src/shell/notification.js:54 msgid "Reply" msgstr "回复" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "é€ščæ‡ GSConnect ē›“ęŽ„å°†é“¾ęŽ„å‘é€åˆ°ęµč§ˆå™Øęˆ–ēŸ­äæ”ć€‚" #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "ęœåŠ”äøåÆē”Ø" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "åœØęµč§ˆå™Øäø­ę‰“å¼€" gnome-shell-extension-gsconnect-50/po/zh_TW.po000066400000000000000000000771561421543444100215400ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: gsconnect\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-10-02 17:35-0700\n" "PO-Revision-Date: 2021-11-09 01:50\n" "Last-Translator: \n" "Language-Team: Chinese Traditional\n" "Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Crowdin-Project: gsconnect\n" "X-Crowdin-Project-ID: 327933\n" "X-Crowdin-Language: zh-TW\n" "X-Crowdin-File: /[GSConnect.gnome-shell-extension-gsconnect] master/po/org.gnome.Shell.Extensions.GSConnect.pot\n" "X-Crowdin-File-ID: 13\n" #. TRANSLATORS: Extension name #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:5 #: webextension/gettext.js:29 msgid "GSConnect" msgstr "GSConnect" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:6 msgid "KDE Connect implementation for GNOME" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:16 msgid "GSConnect Team" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:32 msgid "GSConnect is a complete implementation of KDE Connect especially for GNOME Shell with Nautilus, Chrome and Firefox integration. The KDE Connect team has applications for Linux, BSD, Android, Sailfish, macOS and Windows." msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:35 msgid "With GSConnect you can securely connect to mobile devices and other desktops to:" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:39 msgid "Share files, links and text" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:40 msgid "Send and receive messages" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:41 msgid "Sync clipboard content" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:42 msgid "Sync contacts" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:43 msgid "Sync notifications" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:44 msgid "Control media players" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:45 msgid "Control system volume" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:46 msgid "Execute predefined commands" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:47 msgid "And more…" msgstr "" #: data/metainfo/org.gnome.Shell.Extensions.GSConnect.metainfo.xml.in:102 msgid "GSConnect in GNOME Shell" msgstr "" #. TRANSLATORS: Open a dialog to connect to an IP or Bluez device #: data/ui/connect-dialog.ui:13 data/ui/preferences-window.ui:719 msgid "Connect to…" msgstr "é€£ē·šåˆ°ā€¦" #: data/ui/connect-dialog.ui:19 data/ui/legacy-messaging-dialog.ui:13 #: data/ui/legacy-messaging-dialog.ui:17 #: data/ui/notification-reply-dialog.ui:12 #: data/ui/notification-reply-dialog.ui:16 #: data/ui/preferences-command-editor.ui:14 #: data/ui/preferences-command-editor.ui:150 #: data/ui/preferences-shortcut-editor.ui:12 #: data/ui/service-device-chooser.ui:14 data/ui/service-device-chooser.ui:18 #: data/ui/service-error-dialog.ui:13 data/ui/service-error-dialog.ui:21 #: src/preferences/service.js:413 src/service/plugins/share.js:159 #: src/service/plugins/share.js:287 src/service/plugins/share.js:418 msgid "Cancel" msgstr "å–ę¶ˆ" #: data/ui/connect-dialog.ui:26 msgid "Connect" msgstr "é€£ē·š" #: data/ui/connect-dialog.ui:73 msgid "IP Address" msgstr "IP 位址" #: data/ui/contact-chooser.ui:49 msgid "No contacts" msgstr "ę²’ęœ‰čÆēµ”äŗŗ" #: data/ui/contact-chooser.ui:61 data/ui/messaging-window.ui:96 #: data/ui/mousepad-input-dialog.ui:49 data/ui/preferences-window.ui:745 msgid "Help" msgstr "幫助" #: data/ui/contact-chooser.ui:102 msgid "Type a phone number or name" msgstr "č¼øå…„é›»č©±č™Ÿē¢¼ęˆ–å§“å" #. TRANSLATORS: All other phone number types #: data/ui/contacts-address-row.ui:64 src/service/ui/contacts.js:145 msgid "Other" msgstr "" #. TRANSLATORS: Share URL by SMS #: data/ui/legacy-messaging-dialog.ui:8 src/service/daemon.js:288 #: src/service/daemon.js:402 src/service/plugins/sms.js:60 #: webextension/gettext.js:41 msgid "Send SMS" msgstr "ē™¼é€ē°”čØŠ" #: data/ui/legacy-messaging-dialog.ui:25 data/ui/legacy-messaging-dialog.ui:29 #: data/ui/notification-reply-dialog.ui:24 #: data/ui/notification-reply-dialog.ui:28 src/service/plugins/share.js:419 msgid "Send" msgstr "傳送" #: data/ui/legacy-messaging-dialog.ui:62 data/ui/messaging-window.ui:256 #: data/ui/notification-reply-dialog.ui:61 msgid "Device is disconnected" msgstr "設備已斷開" #: data/ui/messaging-conversation.ui:84 src/service/plugins/sms.js:52 msgid "Send Message" msgstr "å‚³é€čØŠęÆ" #: data/ui/messaging-conversation.ui:85 src/shell/notification.js:69 msgid "Type a message" msgstr "č¼øå…„čØŠęÆ" #: data/ui/messaging-conversation.ui:92 msgid "Message Entry" msgstr "" #: data/ui/messaging-conversation.ui:93 msgid "Type a message and press Enter to send" msgstr "" #: data/ui/messaging-window.ui:14 src/service/plugins/sms.js:28 #: src/service/ui/messaging.js:1049 msgid "Messaging" msgstr "ē™¼é€ē°”čØŠ" #: data/ui/messaging-window.ui:23 data/ui/messaging-window.ui:36 #: src/service/ui/messaging.js:1260 msgid "New Conversation" msgstr "ę–°ēš„å°č©±" #: data/ui/messaging-window.ui:113 msgid "No Conversations" msgstr "ę²’ęœ‰å°č©±" #: data/ui/messaging-window.ui:173 msgid "No conversation selected" msgstr "å°šē„”å°č©±" #: data/ui/messaging-window.ui:189 msgid "Select or start a conversation" msgstr "éøę“‡ęˆ–é–‹å§‹čŠå¤©" #: data/ui/preferences-command-editor.ui:7 msgid "Edit Command" msgstr "" #: data/ui/preferences-command-editor.ui:21 msgid "Save" msgstr "儲存" #: data/ui/preferences-command-editor.ui:54 msgid "Name" msgstr "åēØ±" #: data/ui/preferences-command-editor.ui:92 msgid "Command Line" msgstr "命令列" #: data/ui/preferences-command-editor.ui:114 #: data/ui/preferences-command-editor.ui:143 msgid "Choose an executable" msgstr "éøę“‡åÆåŸ·č”ŒēØ‹å¼" #: data/ui/preferences-command-editor.ui:157 msgid "Open" msgstr "ꉓ開" #: data/ui/preferences-device-panel.ui:46 src/preferences/service.js:490 msgid "Desktop" msgstr "ę”Œé¢" #: data/ui/preferences-device-panel.ui:95 msgid "Camera" msgstr "ę”å½±ę©Ÿ" #: data/ui/preferences-device-panel.ui:152 msgid "Clipboard Sync" msgstr "åŒę­„å‰Ŗč²¼ē°æ" #: data/ui/preferences-device-panel.ui:218 msgid "Media Players" msgstr "媒體播放器" #: data/ui/preferences-device-panel.ui:275 msgid "Mouse & Keyboard" msgstr "滑鼠 & éµē›¤" #: data/ui/preferences-device-panel.ui:332 msgid "Volume Control" msgstr "éŸ³é‡ęŽ§åˆ¶" #: data/ui/preferences-device-panel.ui:386 src/service/plugins/sftp.js:379 msgid "Files" msgstr "ęŖ”ę”ˆ" #: data/ui/preferences-device-panel.ui:438 msgid "Receive Files" msgstr "ęŽ„ę”¶ęŖ”ę”ˆ" #: data/ui/preferences-device-panel.ui:497 msgid "Save files to" msgstr "" #: data/ui/preferences-device-panel.ui:558 #: data/ui/preferences-device-panel.ui:2219 msgid "Sharing" msgstr "åˆ†äŗ«" #: data/ui/preferences-device-panel.ui:589 msgid "Device Battery" msgstr "č£ē½®é›»ę± " #: data/ui/preferences-device-panel.ui:640 msgid "Low Battery Notification" msgstr "ä½Žé›»é‡é€šēŸ„" #: data/ui/preferences-device-panel.ui:699 msgid "Charged Up to Custom Level Notification" msgstr "" #: data/ui/preferences-device-panel.ui:779 msgid "Fully Charged Notification" msgstr "é›»ę± å……é£½é€šēŸ„" #: data/ui/preferences-device-panel.ui:833 msgid "System Battery" msgstr "系統電池" #: data/ui/preferences-device-panel.ui:882 msgid "Share Statistics" msgstr "åˆ†äŗ«ē‹€ę…‹" #: data/ui/preferences-device-panel.ui:936 #: data/ui/preferences-device-panel.ui:2265 src/service/plugins/battery.js:12 msgid "Battery" msgstr "電걠" #: data/ui/preferences-device-panel.ui:966 #: data/ui/preferences-device-panel.ui:1051 #: data/ui/preferences-device-panel.ui:2311 #: src/service/plugins/runcommand.js:24 src/service/plugins/runcommand.js:32 #: src/service/plugins/runcommand.js:192 msgid "Commands" msgstr "ęŒ‡ä»¤" #: data/ui/preferences-device-panel.ui:1025 msgid "Add Command" msgstr "" #: data/ui/preferences-device-panel.ui:1112 msgid "Share Notifications" msgstr "åˆ†äŗ«é€šēŸ„" #: data/ui/preferences-device-panel.ui:1172 msgid "Share When Active" msgstr "" #: data/ui/preferences-device-panel.ui:1223 msgid "Applications" msgstr "ę‡‰ē”ØēØ‹å¼" #: data/ui/preferences-device-panel.ui:1269 #: data/ui/preferences-device-panel.ui:2357 #: src/service/plugins/notification.js:15 msgid "Notifications" msgstr "é€šēŸ„" #: data/ui/preferences-device-panel.ui:1327 src/service/plugins/contacts.js:22 msgid "Contacts" msgstr "聯繫人" #: data/ui/preferences-device-panel.ui:1380 msgid "Incoming Calls" msgstr "來電" #: data/ui/preferences-device-panel.ui:1429 #: data/ui/preferences-device-panel.ui:1596 msgid "Volume" msgstr "音量" #: data/ui/preferences-device-panel.ui:1495 #: data/ui/preferences-device-panel.ui:1662 msgid "Pause Media" msgstr "ęš«åœåŖ’é«”" #: data/ui/preferences-device-panel.ui:1548 msgid "Ongoing Calls" msgstr "é€šč©±äø­" #: data/ui/preferences-device-panel.ui:1718 msgid "Mute Microphone" msgstr "éœéŸ³éŗ„å…‹é¢Ø" #: data/ui/preferences-device-panel.ui:1772 #: data/ui/preferences-device-panel.ui:2403 src/service/plugins/telephony.js:13 msgid "Telephony" msgstr "電話" #: data/ui/preferences-device-panel.ui:1807 msgid "Action Shortcuts" msgstr "ę‡‰ē”Øåæ«ę·éµ" #: data/ui/preferences-device-panel.ui:1823 msgid "Reset All…" msgstr "å…ØéƒØé‡čØ­" #: data/ui/preferences-device-panel.ui:1875 msgid "Shortcuts" msgstr "åæ«ę·éµ" #: data/ui/preferences-device-panel.ui:1906 msgid "Plugins" msgstr "å¤–ęŽ›" #: data/ui/preferences-device-panel.ui:1953 msgid "Experimental" msgstr "實驗性質" #: data/ui/preferences-device-panel.ui:2000 msgid "Device Cache" msgstr "" #: data/ui/preferences-device-panel.ui:2018 msgid "Clear Cache…" msgstr "" #: data/ui/preferences-device-panel.ui:2057 msgid "Legacy SMS Support" msgstr "čˆŠē‰ˆē°”čØŠę”Æę“" #: data/ui/preferences-device-panel.ui:2114 msgid "SFTP Automount" msgstr "" #: data/ui/preferences-device-panel.ui:2169 #: data/ui/preferences-device-panel.ui:2495 msgid "Advanced" msgstr "進階" #: data/ui/preferences-device-panel.ui:2449 msgid "Keyboard Shortcuts" msgstr "éµē›¤åæ«ę·éµ" #: data/ui/preferences-device-panel.ui:2513 msgid "Device Settings" msgstr "" #. TRANSLATORS: Send a pair request to the device #: data/ui/preferences-device-panel.ui:2557 #: data/ui/preferences-device-panel.ui:2649 src/service/daemon.js:381 msgid "Pair" msgstr "配對" #: data/ui/preferences-device-panel.ui:2589 msgid "Device is unpaired" msgstr "č£ē½®å·²å–ę¶ˆé…å°" #: data/ui/preferences-device-panel.ui:2604 msgid "You may configure this device before pairing" msgstr "ę‚ØåÆä»„åœØé…å°ę­¤č£ē½®å‰é€²č”ŒčØ­ē½®" #. TRANSLATORS: View the TLS Certificate fingerprint #: data/ui/preferences-device-panel.ui:2644 src/preferences/device.js:384 msgid "Encryption Info" msgstr "åŠ åÆ†č³‡čØŠ" #. TRANSLATORS: Unpair the device and notify it #: data/ui/preferences-device-panel.ui:2655 src/service/daemon.js:390 msgid "Unpair" msgstr "å–ę¶ˆé…å°" #. TRANSLATORS: Send clipboard content to device #: data/ui/preferences-device-panel.ui:2667 msgid "To Device" msgstr "åˆ°č£ē½®" #. TRANSLATORS: Receive clipboard content from the device #: data/ui/preferences-device-panel.ui:2673 msgid "From Device" msgstr "å¾žč£ē½®" #. TRANSLATORS: Don't change the system volume #: data/ui/preferences-device-panel.ui:2685 #: data/ui/preferences-device-panel.ui:2718 msgid "Nothing" msgstr "ē„”å‹•ä½œ" #. TRANSLATORS: Restore the system volume #: data/ui/preferences-device-panel.ui:2692 #: data/ui/preferences-device-panel.ui:2725 msgid "Restore" msgstr "" #. TRANSLATORS: Lower the system volume #: data/ui/preferences-device-panel.ui:2699 #: data/ui/preferences-device-panel.ui:2732 msgid "Lower" msgstr "降低音量" #. TRANSLATORS: Mute the system volume #. TRANSLATORS: Silence the actively ringing call #: data/ui/preferences-device-panel.ui:2706 #: data/ui/preferences-device-panel.ui:2739 #: src/service/plugins/telephony.js:195 msgid "Mute" msgstr "靜音" #: data/ui/preferences-shortcut-editor.ui:18 msgid "Set" msgstr "設定" #. Keys for cancelling (␛) or resetting (␈) a shortcut #: data/ui/preferences-shortcut-editor.ui:73 msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut." msgstr "ęŒ‰äø‹ Esc å–ę¶ˆåæ«ę·éµļ¼Œęˆ–ęŒ‰äø‹å€’é€€éµļ¼ˆBackspaceļ¼‰é‡čØ­åæ«ę·éµć€‚" #: data/ui/preferences-window.ui:17 msgid "Device Name" msgstr "č£ē½®åēØ±" #: data/ui/preferences-window.ui:54 msgid "_Rename" msgstr "é‡ę–°å‘½åļ¼ˆ_R)" #: data/ui/preferences-window.ui:91 data/ui/preferences-window.ui:105 msgid "Refresh" msgstr "é‡ę–°ę•“ē†" #. Service Menu -> "Mobile Settings" #: data/ui/preferences-window.ui:132 src/extension.js:124 msgid "Mobile Settings" msgstr "ę‰‹ę©ŸčØ­ē½®" #: data/ui/preferences-window.ui:159 msgid "Service Menu" msgstr "" #: data/ui/preferences-window.ui:182 msgid "Device Menu" msgstr "" #: data/ui/preferences-window.ui:196 data/ui/preferences-window.ui:211 msgid "Edit Device Name" msgstr "äæ®ę”¹č£ē½®åēØ±" #: data/ui/preferences-window.ui:271 msgid "Devices" msgstr "č£ē½®" #: data/ui/preferences-window.ui:321 src/preferences/service.js:652 msgid "Searching for devices…" msgstr "ę­£åœØęœå°‹č£ē½®ā€¦" #: data/ui/preferences-window.ui:346 msgid "Browser Add-Ons" msgstr "ē€č¦½å™Øå¤–ęŽ›" #: data/ui/preferences-window.ui:626 msgid "Enable" msgstr "å•Ÿē”Ø" #: data/ui/preferences-window.ui:658 msgid "This device is invisible to unpaired devices" msgstr "ęœŖé…å°č£ē½®ēœ‹äøåˆ°ę­¤č£ē½®" #: data/ui/preferences-window.ui:670 src/service/manager.js:113 msgid "Discovery Disabled" msgstr "ē¦ę­¢č¢«ęŽ¢ē“¢" #: data/ui/preferences-window.ui:724 msgid "Display Mode" msgstr "é”Æē¤ŗęØ”å¼" #. TRANSLATORS: Show device indicators in the top bar #: data/ui/preferences-window.ui:727 msgid "Panel" msgstr "é ‚ē«Æåˆ—" #. TRANSLATORS: Show devices in the user menu like Bluetooth #: data/ui/preferences-window.ui:733 msgid "User Menu" msgstr "使用者選單" #. TRANSLATORS: Generate a support log #: data/ui/preferences-window.ui:741 src/preferences/service.js:410 msgid "Generate Support Log" msgstr "ē”¢ē”Ÿę”Æę“ Log" #: data/ui/preferences-window.ui:749 msgid "About GSConnect" msgstr "é—œę–¼ GSConnect" #: data/ui/service-device-chooser.ui:8 msgid "Select a Device" msgstr "éøę“‡č£ē½®" #: data/ui/service-device-chooser.ui:26 data/ui/service-device-chooser.ui:31 msgid "Select" msgstr "選擇" #. TRANSLATORS: No devices are known or available #: data/ui/service-device-chooser.ui:94 webextension/gettext.js:37 msgid "No Device Found" msgstr "ę‰¾äøåˆ°č£ē½®" #: data/ui/service-device-chooser.ui:111 msgid "Device List" msgstr "" #: data/ui/service-error-dialog.ui:32 data/ui/service-error-dialog.ui:40 msgid "Report" msgstr "回報" #: data/ui/service-error-dialog.ui:72 msgid "Something’s gone wrong" msgstr "" #: data/ui/service-error-dialog.ui:84 msgid "GSConnect encountered an unexpected error. Please report the problem and include any information that may help." msgstr "" #: data/ui/service-error-dialog.ui:118 msgid "Technical Details" msgstr "" #. TRANSLATORS: Top-level context menu item for GSConnect #: nautilus-extension/nautilus-gsconnect.py:164 webextension/gettext.js:33 msgid "Send To Mobile Device" msgstr "å‚³é€åˆ°ę‰‹ę©Ÿ" #. Service Menu #: src/extension.js:91 src/extension.js:237 msgid "Mobile Devices" msgstr "č”Œå‹•č£ē½®" #. TRANSLATORS: A menu option to activate the extension #: src/extension.js:119 src/extension.js:380 msgid "Turn On" msgstr "開啟" #: src/extension.js:232 #, javascript-format msgid "%d Connected" msgid_plural "%d Connected" msgstr[0] "%d å·²é€£ē·š" #. TRANSLATORS: A menu option to deactivate the extension #: src/extension.js:377 msgid "Turn Off" msgstr "關閉" #: src/preferences/device.js:666 src/preferences/device.js:672 msgid "Edit" msgstr "編輯" #: src/preferences/device.js:681 src/preferences/device.js:687 msgid "Remove" msgstr "移除" #: src/preferences/device.js:941 src/preferences/device.js:969 msgid "Disabled" msgstr "åœē”Ø" #. TRANSLATORS: Summary of a keyboard shortcut function #. Example: Enter a new shortcut to change Messaging #: src/preferences/keybindings.js:64 #, javascript-format msgid "Enter a new shortcut to change %s" msgstr "č¼øå…„ę–°ēš„åæ«ę·éµå–ä»£ %s" #. TRANSLATORS: When a keyboard shortcut is unavailable #. Example: [Ctrl]+[S] is already being used #: src/preferences/keybindings.js:130 #, javascript-format msgid "%s is already being used" msgstr "怌%sć€å·²ē¶“č¢«ä½æē”Ø" #: src/preferences/service.js:369 msgid "A complete KDE Connect implementation for GNOME" msgstr "一個為 GNOME å®Œę•“åÆ¦åšēš„ KDE Connect" #. TRANSLATORS: eg. 'Translator Name ' #: src/preferences/service.js:378 msgid "translator-credits" msgstr "翻譯組哔" #: src/preferences/service.js:411 msgid "Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log." msgstr "é™¤éŒÆčØŠęÆå·²č¢«ē“€éŒ„ć€‚č«‹åŸ·č”Œä»»ä½•åÆčƒ½é€ ęˆå•é”Œēš„å‹•ä½œļ¼Œē„¶å¾ŒęŸ„ēœ‹ę—„čŖŒć€‚" #: src/preferences/service.js:414 msgid "Review Log" msgstr "ęŸ„ēœ‹ę—„čŖŒ" #: src/preferences/service.js:482 msgid "Laptop" msgstr "筆電" #: src/preferences/service.js:484 msgid "Smartphone" msgstr "ę™ŗę…§åž‹ę‰‹ę©Ÿ" #: src/preferences/service.js:486 msgid "Tablet" msgstr "å¹³ęæ" #: src/preferences/service.js:488 msgid "Television" msgstr "電視" #: src/preferences/service.js:510 msgid "Unpaired" msgstr "ęœŖé…å°" #: src/preferences/service.js:514 msgid "Disconnected" msgstr "å·²äø­ę–·é€£ē·š" #: src/preferences/service.js:518 msgid "Connected" msgstr "å·²é€£ęŽ„" #: src/preferences/service.js:654 msgid "Waiting for service…" msgstr "ē­‰å¾…ä¼ŗęœå™Øā€¦" #: src/service/daemon.js:189 msgid "Click for help troubleshooting" msgstr "é»žę“Šä»„å¹«åŠ©é™¤éŒÆ" #: src/service/daemon.js:200 msgid "Click for more information" msgstr "ęŒ‰äø€äø‹ä»„ēž­č§£č©³ęƒ…" #: src/service/daemon.js:294 msgid "Dial Number" msgstr "ꒄꉓ電話" #: src/service/daemon.js:300 src/service/daemon.js:498 #: src/service/plugins/share.js:29 msgid "Share File" msgstr "åˆ†äŗ«ęŖ”ę”ˆ" #: src/service/daemon.js:351 msgid "List available devices" msgstr "åˆ—å‡ŗåÆē”Øč£ē½®" #: src/service/daemon.js:360 msgid "List all devices" msgstr "åˆ—å‡ŗę‰€ęœ‰č£ē½®" #: src/service/daemon.js:369 msgid "Target Device" msgstr "ē›®ęØ™č£ē½®" #: src/service/daemon.js:411 msgid "Message Body" msgstr "čØŠęÆå…§ę–‡" #: src/service/daemon.js:423 src/service/plugins/notification.js:54 msgid "Send Notification" msgstr "ē™¼é€é€šēŸ„" #: src/service/daemon.js:432 msgid "Notification App Name" msgstr "ēØ‹å¼åēØ±é€šēŸ„" #: src/service/daemon.js:441 msgid "Notification Body" msgstr "é€šēŸ„ę¬„ęœ¬čŗ«" #: src/service/daemon.js:450 msgid "Notification Icon" msgstr "é€šēŸ„åœ–ē¤ŗ" #: src/service/daemon.js:459 msgid "Notification ID" msgstr "ID é€šēŸ„" #: src/service/daemon.js:468 src/service/plugins/photo.js:12 #: src/service/plugins/photo.js:25 msgid "Photo" msgstr "ꋍꔝ" #: src/service/daemon.js:477 src/service/plugins/ping.js:11 #: src/service/plugins/ping.js:18 src/service/plugins/ping.js:45 msgid "Ping" msgstr "Ping" #: src/service/daemon.js:486 src/service/plugins/battery.js:244 #: src/service/plugins/battery.js:273 src/service/plugins/battery.js:302 #: src/service/plugins/findmyphone.js:20 msgid "Ring" msgstr "響音" #: src/service/daemon.js:507 src/service/plugins/share.js:45 #: src/service/ui/messaging.js:1243 src/service/ui/messaging.js:1251 msgid "Share Link" msgstr "å…±äŗ«éˆēµ" #: src/service/daemon.js:516 src/service/plugins/share.js:37 msgid "Share Text" msgstr "åˆ†äŗ«ę–‡å­—" #: src/service/daemon.js:528 msgid "Show release version" msgstr "é”Æē¤ŗē‰ˆęœ¬" #: src/service/device.js:162 src/service/device.js:163 #: src/service/ui/messaging.js:393 msgid "Not available" msgstr "焔法使用" #. TRANSLATORS: Bluetooth address for remote device #: src/service/device.js:168 #, javascript-format msgid "Bluetooth device at %s" msgstr "åœØć€Œ%sć€ēš„č—ē‰™č£ē½®" #. TRANSLATORS: Label for TLS Certificate fingerprint #. #. Example: #. #. Google Pixel Fingerprint: #. 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 #: src/service/device.js:197 src/service/device.js:199 #, javascript-format msgid "%s Fingerprint:" msgstr "怌%sć€ę•øä½ęŒ‡ē“‹ļ¼š" #. TRANSLATORS: eg. Pair Request from Google Pixel #: src/service/device.js:793 #, javascript-format msgid "Pair Request from %s" msgstr "怌%sć€ēš„é…å°č«‹ę±‚" #: src/service/device.js:800 msgid "Reject" msgstr "拒絕" #: src/service/device.js:805 msgid "Accept" msgstr "ęŽ„å—" #: src/service/manager.js:114 msgid "Discovery has been disabled due to the number of devices on this network." msgstr "å› ē‚ŗę­¤ē¶²åŸŸå…§ēš„č£ē½®ę•øé‡ļ¼ŒęŽ¢ē“¢å·²åœē”Ø" #: src/service/backends/lan.js:162 msgid "OpenSSL not found" msgstr "" #: src/service/backends/lan.js:452 msgid "Port already in use" msgstr "" #: src/service/plugins/battery.js:13 msgid "Exchange battery information" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is full #: src/service/plugins/battery.js:253 #, javascript-format msgid "%s: Battery is full" msgstr "%sļ¼šé›»ę± å·²å……é£½" #. TRANSLATORS: when the battery is fully charged #. TRANSLATORS: When the battery level is 100% #: src/service/plugins/battery.js:255 src/shell/device.js:118 msgid "Fully Charged" msgstr "å……é›»å®Œęˆ" #. TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level #: src/service/plugins/battery.js:282 #, javascript-format msgid "%s: Battery has reached custom charge level" msgstr "" #. TRANSLATORS: when the battery has reached custom charge level #: src/service/plugins/battery.js:284 #, javascript-format msgid "%d%% Charged" msgstr "" #. TRANSLATORS: eg. Google Pixel: Battery is low #: src/service/plugins/battery.js:311 #, javascript-format msgid "%s: Battery is low" msgstr "%sļ¼šé›»é‡éŽä½Ž" #. TRANSLATORS: eg. 15% remaining #: src/service/plugins/battery.js:313 #, javascript-format msgid "%d%% remaining" msgstr "剩下 %d%%" #: src/service/plugins/clipboard.js:10 msgid "Clipboard" msgstr "剪貼簿" #: src/service/plugins/clipboard.js:11 msgid "Share the clipboard content" msgstr "" #: src/service/plugins/clipboard.js:23 msgid "Clipboard Push" msgstr "ęŽØé€å‰Ŗč²¼ē°æ" #: src/service/plugins/clipboard.js:31 msgid "Clipboard Pull" msgstr "ęŽ„ę”¶å‰Ŗč²¼ē°æ" #: src/service/plugins/contacts.js:23 msgid "Access contacts of the paired device" msgstr "" #: src/service/plugins/findmyphone.js:13 msgid "Find My Phone" msgstr "ę‰¾å°‹ęˆ‘ēš„ę‰‹ę©Ÿ" #: src/service/plugins/findmyphone.js:14 msgid "Ring your paired device" msgstr "" #: src/service/plugins/mousepad.js:12 msgid "Mousepad" msgstr "č§øęŽ§ęæ" #: src/service/plugins/mousepad.js:13 msgid "Enables the paired device to act as a remote mouse and keyboard" msgstr "" #: src/service/plugins/mousepad.js:27 src/service/ui/mousepad.js:102 msgid "Keyboard" msgstr "éµē›¤" #: src/service/plugins/mpris.js:15 msgid "MPRIS" msgstr "MPRIS" #: src/service/plugins/mpris.js:16 msgid "Bidirectional remote media playback control" msgstr "" #: src/service/plugins/mpris.js:316 msgid "Unknown" msgstr "" #: src/service/plugins/notification.js:16 msgid "Share notifications with the paired device" msgstr "" #: src/service/plugins/notification.js:30 msgid "Cancel Notification" msgstr "å–ę¶ˆé€šēŸ„" #: src/service/plugins/notification.js:38 msgid "Close Notification" msgstr "é—œé–‰é€šēŸ„" #: src/service/plugins/notification.js:46 msgid "Reply Notification" msgstr "å›žč¦†é€šēŸ„" #: src/service/plugins/notification.js:62 msgid "Activate Notification" msgstr "å•Ÿē”Øé€šēŸ„" #: src/service/plugins/photo.js:13 msgid "Request the paired device to take a photo and transfer it to this PC" msgstr "" #: src/service/plugins/photo.js:220 src/service/plugins/share.js:128 #: src/service/plugins/share.js:200 src/service/plugins/share.js:310 msgid "Transfer Failed" msgstr "傳輸失敗" #. TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel #. TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel #: src/service/plugins/photo.js:222 src/service/plugins/share.js:312 #, javascript-format msgid "Failed to send ā€œ%sā€ to %s" msgstr "å‚³é€ć€Œ%sć€åˆ°ć€Œ%sć€å¤±ę•—" #: src/service/plugins/ping.js:12 msgid "Send and receive pings" msgstr "" #. TRANSLATORS: An optional message accompanying a ping, rarely if ever used #. eg. Ping: A message sent with ping #: src/service/plugins/ping.js:52 #, javascript-format msgid "Ping: %s" msgstr "Ping: %s" #: src/service/plugins/presenter.js:10 msgid "Presentation" msgstr "" #: src/service/plugins/presenter.js:11 msgid "Use the paired device as a presenter" msgstr "" #: src/service/plugins/runcommand.js:11 msgid "Run Commands" msgstr "åŸ·č”ŒęŒ‡ä»¤" #: src/service/plugins/runcommand.js:13 msgid "Run commands on your paired device or let the device run predefined commands on this PC" msgstr "" #: src/service/plugins/sftp.js:13 msgid "SFTP" msgstr "SFTP" #: src/service/plugins/sftp.js:15 msgid "Browse the paired device filesystem" msgstr "" #: src/service/plugins/sftp.js:20 msgid "Mount" msgstr "ęŽ›č¼‰" #: src/service/plugins/sftp.js:28 msgid "Unmount" msgstr "åøč¼‰" #: src/service/plugins/sftp.js:212 #, javascript-format msgid "%s reported an error" msgstr "" #: src/service/plugins/share.js:14 src/service/plugins/share.js:21 msgid "Share" msgstr "共享" #: src/service/plugins/share.js:16 msgid "Share files and URLs between devices" msgstr "" #. TRANSLATORS: eg. Google Pixel is not allowed to upload files #: src/service/plugins/share.js:130 #, javascript-format msgid "%s is not allowed to upload files" msgstr "äøå…čØ±ć€Œ%sć€äøŠå‚³ęŖ”ę”ˆ" #: src/service/plugins/share.js:152 src/service/plugins/share.js:280 msgid "Transferring File" msgstr "ęŖ”ę”ˆå‚³é€äø­" #. TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel #: src/service/plugins/share.js:154 #, javascript-format msgid "Receiving ā€œ%sā€ from %s" msgstr "ę­£åœØęŽ„ę”¶ć€Œ%sć€ļ¼Œä¾†č‡Ŗę–¼ć€Œ%s怍" #: src/service/plugins/share.js:173 src/service/plugins/share.js:300 msgid "Transfer Successful" msgstr "å‚³č¼øęˆåŠŸ" #. TRANSLATORS: eg. Received 'book.pdf' from Google Pixel #: src/service/plugins/share.js:175 #, javascript-format msgid "Received ā€œ%sā€ from %s" msgstr "å·²ęŽ„ę”¶ć€Œ%sć€ļ¼Œä¾†č‡Ŗę–¼ć€Œ%s怍" #: src/service/plugins/share.js:181 msgid "Open Folder" msgstr "打開資料夾" #: src/service/plugins/share.js:186 msgid "Open File" msgstr "ę‰“é–‹ęŖ”ę”ˆ" #. TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel #: src/service/plugins/share.js:202 #, javascript-format msgid "Failed to receive ā€œ%sā€ from %s" msgstr "ęŽ„ę”¶ć€Œ%sć€å¤±ę•—ļ¼Œä¾†č‡Ŗę–¼ć€Œ%s怍" #: src/service/plugins/share.js:232 #, javascript-format msgid "Text Shared By %s" msgstr "怌%sć€åˆ†äŗ«ēš„ę–‡å­—" #. TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel #: src/service/plugins/share.js:282 #, javascript-format msgid "Sending ā€œ%sā€ to %s" msgstr "ę­£åœØå°‡ć€Œ%sć€å‚³é€åˆ°ć€Œ%s怍" #. TRANSLATORS: eg. Sent "book.pdf" to Google Pixel #: src/service/plugins/share.js:302 #, javascript-format msgid "Sent ā€œ%sā€ to %s" msgstr "怌%sć€å·²å‚³é€åˆ°ć€Œ%s怍" #. TRANSLATORS: eg. Send files to Google Pixel #: src/service/plugins/share.js:370 #, javascript-format msgid "Send files to %s" msgstr "å°‡ęŖ”ę”ˆå‚³é€åˆ°ć€Œ%s怍" #. TRANSLATORS: Mark the file to be opened once completed #: src/service/plugins/share.js:374 msgid "Open when done" msgstr "ē•¶å®Œęˆę™‚é–‹å•Ÿ" #. TRANSLATORS: eg. Send a link to Google Pixel #: src/service/plugins/share.js:413 #, javascript-format msgid "Send a link to %s" msgstr "ē™¼é€äø€å€‹éˆēµåˆ°ć€Œ%s怍" #: src/service/plugins/sms.js:14 msgid "SMS" msgstr "簔訊" #: src/service/plugins/sms.js:15 msgid "Send and read SMS of the paired device and be notified of new SMS" msgstr "" #: src/service/plugins/sms.js:36 msgid "New SMS (URI)" msgstr "ę–°å¢žē°”čØŠļ¼ˆURI)" #: src/service/plugins/sms.js:44 msgid "Reply SMS" msgstr "å›žč¦†ē°”čØŠ" #: src/service/plugins/sms.js:68 msgid "Share SMS" msgstr "åˆ†äŗ«ē°”čØŠ" #: src/service/plugins/systemvolume.js:11 msgid "System Volume" msgstr "ē³»ēµ±éŸ³é‡" #: src/service/plugins/systemvolume.js:12 msgid "Enable the paired device to control the system volume" msgstr "" #: src/service/plugins/systemvolume.js:56 msgid "PulseAudio not found" msgstr "" #: src/service/plugins/telephony.js:14 msgid "Be notified about calls and adjust system volume during ringing/ongoing calls" msgstr "" #. TRANSLATORS: Silence the actively ringing call #: src/service/plugins/telephony.js:26 msgid "Mute Call" msgstr "é€šč©±éœéŸ³" #. Ensure we have a sender #. TRANSLATORS: No name or phone number #. Contact Name #: src/service/plugins/telephony.js:153 src/service/plugins/telephony.js:172 #: src/service/ui/contacts.js:607 src/service/ui/messaging.js:742 msgid "Unknown Contact" msgstr "ęœŖēŸ„ēš„čÆēµ”äŗŗ" #. TRANSLATORS: The phone is ringing #: src/service/plugins/telephony.js:191 msgid "Incoming call" msgstr "來電" #. TRANSLATORS: A phone call is active #: src/service/plugins/telephony.js:206 msgid "Ongoing call" msgstr "é€šč©±äø­" #. TRANSLATORS: A fax number #: src/service/ui/contacts.js:130 msgid "Fax" msgstr "" #. TRANSLATORS: A work or office phone number #: src/service/ui/contacts.js:134 msgid "Work" msgstr "" #. TRANSLATORS: A mobile or cellular phone number #: src/service/ui/contacts.js:138 msgid "Mobile" msgstr "" #. TRANSLATORS: A home phone number #: src/service/ui/contacts.js:142 msgid "Home" msgstr "" #. TRANSLATORS: A phone number (eg. "Send to 555-5555") #. Update UI #: src/service/ui/contacts.js:505 src/service/ui/contacts.js:520 #, javascript-format msgid "Send to %s" msgstr "å‚³é€åˆ°ć€Œ%s怍" #. TRANSLATORS: Less than a minute ago #: src/service/ui/messaging.js:100 src/service/ui/messaging.js:141 msgid "Just now" msgstr "å°±åœØå‰›ę‰" #. TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) #: src/service/ui/messaging.js:109 #, javascript-format msgid "Yesterday惻%s" msgstr "昨天・%s" #: src/service/ui/messaging.js:146 #, javascript-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "%d 分鐘" #: src/service/ui/messaging.js:750 msgid "Group Message" msgstr "ē¾¤ēµ„ē°”čØŠ" #. TRANSLATORS: An outgoing message body in a conversation summary #: src/service/ui/messaging.js:765 #, javascript-format msgid "You: %s" msgstr "你:%s" #: src/service/ui/messaging.js:951 #, javascript-format msgid "And %d other contact" msgid_plural "And %d others" msgstr[0] "和 %d ä½å…¶ä»–čÆēµ”äŗŗ" #. TRANSLATORS: Displayed when the remote keyboard is not ready to accept input #: src/service/ui/mousepad.js:110 #, javascript-format msgid "Remote keyboard on %s is not active" msgstr "" #. TRANSLATORS: When no time estimate for the battery is available #. EXAMPLE: 42% (Estimating…) #: src/shell/device.js:123 #, javascript-format msgid "%d%% (Estimating…)" msgstr "%d%% (čØˆē®—äø­ā€¦)" #. TRANSLATORS: Estimated time until battery is charged #. EXAMPLE: 42% (1:15 Until Full) #: src/shell/device.js:132 #, javascript-format msgid "%d%% (%d∶%02d Until Full)" msgstr "%d%%ļ¼ˆč·é›¢å……ę»æé‚„éœ€ %d∶%02d )" #. TRANSLATORS: Estimated time until battery is empty #. EXAMPLE: 42% (12:15 Remaining) #: src/shell/device.js:140 #, javascript-format msgid "%d%% (%d∶%02d Remaining)" msgstr "%d%% (%d∶%02d 剩餘)" #: src/shell/notification.js:54 msgid "Reply" msgstr "å›žč¦†" #. TRANSLATORS: Chrome/Firefox WebExtension description #: webextension/gettext.js:31 msgid "Share links with GSConnect, direct to the browser or by SMS." msgstr "ē›“ęŽ„ē”Øē€č¦½å™Øęˆ–ē¶“ē”±ē°”čØŠå’Œ GSConnect å…±äŗ«éˆēµ" #. TRANSLATORS: WebExtension can't connect to GSConnect #: webextension/gettext.js:35 msgid "Service Unavailable" msgstr "ęœå‹™ē„”ę³•ä½æē”Ø" #. TRANSLATORS: Open URL with the device's browser #: webextension/gettext.js:39 msgid "Open in Browser" msgstr "ä»„ē€č¦½å™Øé–‹å•Ÿ" gnome-shell-extension-gsconnect-50/src/000077500000000000000000000000001421543444100202765ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/src/extension.js000066400000000000000000000356021421543444100226560ustar00rootroot00000000000000'use strict'; const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Main = imports.ui.main; const PanelMenu = imports.ui.panelMenu; const PopupMenu = imports.ui.popupMenu; const AggregateMenu = Main.panel.statusArea.aggregateMenu; // Bootstrap const Extension = imports.misc.extensionUtils.getCurrentExtension(); const Utils = Extension.imports.shell.utils; // eslint-disable-next-line no-redeclare const _ = Extension._; const Clipboard = Extension.imports.shell.clipboard; const Config = Extension.imports.config; const Device = Extension.imports.shell.device; const Keybindings = Extension.imports.shell.keybindings; const Notification = Extension.imports.shell.notification; const Remote = Extension.imports.utils.remote; Extension.getIcon = Utils.getIcon; /** * A System Indicator used as the hub for spawning device indicators and * indicating that the extension is active when there are none. */ const ServiceIndicator = GObject.registerClass({ GTypeName: 'GSConnectServiceIndicator', }, class ServiceIndicator extends PanelMenu.SystemIndicator { _init() { super._init(); this._menus = {}; this._keybindings = new Keybindings.Manager(); // GSettings this.settings = new Gio.Settings({ settings_schema: Config.GSCHEMA.lookup( 'org.gnome.Shell.Extensions.GSConnect', null ), path: '/org/gnome/shell/extensions/gsconnect/', }); this._enabledId = this.settings.connect( 'changed::enabled', this._onEnabledChanged.bind(this) ); this._panelModeId = this.settings.connect( 'changed::show-indicators', this._sync.bind(this) ); // Service Proxy this.service = new Remote.Service(); this._deviceAddedId = this.service.connect( 'device-added', this._onDeviceAdded.bind(this) ); this._deviceRemovedId = this.service.connect( 'device-removed', this._onDeviceRemoved.bind(this) ); this._serviceChangedId = this.service.connect( 'notify::active', this._onServiceChanged.bind(this) ); // Service Indicator this._indicator = this._addIndicator(); this._indicator.gicon = Extension.getIcon( 'org.gnome.Shell.Extensions.GSConnect-symbolic' ); this._indicator.visible = false; AggregateMenu._indicators.insert_child_at_index(this, 0); AggregateMenu._gsconnect = this; // Service Menu this._item = new PopupMenu.PopupSubMenuMenuItem(_('Mobile Devices'), true); this._item.icon.gicon = this._indicator.gicon; this._item.label.clutter_text.x_expand = true; this.menu.addMenuItem(this._item); // Find current index of network menu const menuItems = AggregateMenu.menu._getMenuItems(); const networkMenuIndex = AggregateMenu._network ? menuItems.indexOf(AggregateMenu._network.menu) : -1; const menuIndex = networkMenuIndex > -1 ? networkMenuIndex : 3; // Place our menu below the network menu AggregateMenu.menu.addMenuItem(this.menu, menuIndex + 1); // Service Menu -> Devices Section this.deviceSection = new PopupMenu.PopupMenuSection(); this.deviceSection.actor.add_style_class_name('gsconnect-device-section'); this.settings.bind( 'show-indicators', this.deviceSection.actor, 'visible', Gio.SettingsBindFlags.INVERT_BOOLEAN ); this._item.menu.addMenuItem(this.deviceSection); // Service Menu -> Separator this._item.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); // Service Menu -> "Turn On/Off" this._enableItem = this._item.menu.addAction( _('Turn On'), this._enable.bind(this) ); // Service Menu -> "Mobile Settings" this._item.menu.addAction(_('Mobile Settings'), this._preferences); // Prime the service this._initService(); } async _initService() { try { if (this.settings.get_boolean('enabled')) await this.service.start(); else await this.service.reload(); } catch (e) { logError(e, 'GSConnect'); } } _enable() { try { const enabled = this.settings.get_boolean('enabled'); // If the service state matches the enabled setting, we should // toggle the service by toggling the setting if (this.service.active === enabled) this.settings.set_boolean('enabled', !enabled); // Otherwise, we should change the service to match the setting else if (this.service.active) this.service.stop(); else this.service.start(); } catch (e) { logError(e, 'GSConnect'); } } _preferences() { Gio.Subprocess.new([`${Extension.path}/gsconnect-preferences`], 0); } _sync() { const available = this.service.devices.filter(device => { return (device.connected && device.paired); }); const panelMode = this.settings.get_boolean('show-indicators'); // Hide status indicator if in Panel mode or no devices are available this._indicator.visible = (!panelMode && available.length); // Show device indicators in Panel mode if available for (const device of this.service.devices) { const isAvailable = available.includes(device); const indicator = Main.panel.statusArea[device.g_object_path]; indicator.visible = panelMode && isAvailable; const menu = this._menus[device.g_object_path]; menu.actor.visible = !panelMode && isAvailable; menu._title.actor.visible = !panelMode && isAvailable; } // One connected device in User Menu mode if (!panelMode && available.length === 1) { const device = available[0]; // Hide the menu title and move it to the submenu item this._menus[device.g_object_path]._title.actor.visible = false; this._item.label.text = device.name; // Destroy any other device's signalStrength if (this._item._signalStrength && this._item._signalStrength.device !== device) { this._item._signalStrength.destroy(); this._item._signalStrength = null; } // Add the signalStrength to the submenu item if (!this._item._signalStrength) { this._item._signalStrength = new Device.SignalStrength({ device: device, opacity: 128, }); this._item.actor.insert_child_below( this._item._signalStrength, this._item._triangleBin ); } // Destroy any other device's battery if (this._item._battery && this._item._battery.device !== device) { this._item._battery.destroy(); this._item._battery = null; } // Add the battery to the submenu item if (!this._item._battery) { this._item._battery = new Device.Battery({ device: device, opacity: 128, }); this._item.actor.insert_child_below( this._item._battery, this._item._triangleBin ); } } else { if (available.length > 1) { // TRANSLATORS: %d is the number of devices connected this._item.label.text = Extension.ngettext( '%d Connected', '%d Connected', available.length ).format(available.length); } else { this._item.label.text = _('Mobile Devices'); } // Destroy any battery in the submenu item if (this._item._battery) { this._item._battery.destroy(); this._item._battery = null; } // Destroy any signalStrength in the submenu item if (this._item._signalStrength) { this._item._signalStrength.destroy(); this._item._signalStrength = null; } } } _onDeviceChanged(device, changed, invalidated) { try { const properties = changed.deepUnpack(); if (properties.hasOwnProperty('Connected') || properties.hasOwnProperty('Paired')) this._sync(); } catch (e) { logError(e, 'GSConnect'); } } _onDeviceAdded(service, device) { try { // Device Indicator const indicator = new Device.Indicator({device: device}); Main.panel.addToStatusArea(device.g_object_path, indicator); // Device Menu const menu = new Device.Menu({ device: device, menu_type: 'list', }); this._menus[device.g_object_path] = menu; this.deviceSection.addMenuItem(menu); // Device Settings device.settings = new Gio.Settings({ settings_schema: Config.GSCHEMA.lookup( 'org.gnome.Shell.Extensions.GSConnect.Device', true ), path: `/org/gnome/shell/extensions/gsconnect/device/${device.id}/`, }); // Keyboard Shortcuts device.__keybindingsChangedId = device.settings.connect( 'changed::keybindings', this._onDeviceKeybindingsChanged.bind(this, device) ); this._onDeviceKeybindingsChanged(device); // Watch the for status changes device.__deviceChangedId = device.connect( 'g-properties-changed', this._onDeviceChanged.bind(this) ); this._sync(); } catch (e) { logError(e, 'GSConnect'); } } _onDeviceRemoved(service, device, sync = true) { try { // Stop watching for status changes if (device.__deviceChangedId) device.disconnect(device.__deviceChangedId); // Release keybindings if (device.__keybindingsChangedId) { device.settings.disconnect(device.__keybindingsChangedId); device._keybindings.map(id => this._keybindings.remove(id)); } // Destroy the indicator Main.panel.statusArea[device.g_object_path].destroy(); // Destroy the menu this._menus[device.g_object_path].destroy(); delete this._menus[device.g_object_path]; if (sync) this._sync(); } catch (e) { logError(e, 'GSConnect'); } } _onDeviceKeybindingsChanged(device) { try { // Reset any existing keybindings if (device.hasOwnProperty('_keybindings')) device._keybindings.map(id => this._keybindings.remove(id)); device._keybindings = []; // Get the keybindings const keybindings = device.settings.get_value('keybindings').deepUnpack(); // Apply the keybindings for (const [action, accelerator] of Object.entries(keybindings)) { const [, name, parameter] = Gio.Action.parse_detailed_name(action); const actionId = this._keybindings.add( accelerator, () => device.action_group.activate_action(name, parameter) ); if (actionId !== 0) device._keybindings.push(actionId); } } catch (e) { logError(e, 'GSConnect'); } } async _onEnabledChanged(settings, key) { try { if (this.settings.get_boolean('enabled')) await this.service.start(); else await this.service.stop(); } catch (e) { logError(e, 'GSConnect'); } } async _onServiceChanged(service, pspec) { try { if (this.service.active) { // TRANSLATORS: A menu option to deactivate the extension this._enableItem.label.text = _('Turn Off'); } else { // TRANSLATORS: A menu option to activate the extension this._enableItem.label.text = _('Turn On'); // If it's enabled, we should try to restart now if (this.settings.get_boolean('enabled')) await this.service.start(); } } catch (e) { logError(e, 'GSConnect'); } } destroy() { // Unhook from Remote.Service if (this.service) { this.service.disconnect(this._serviceChangedId); this.service.disconnect(this._deviceAddedId); this.service.disconnect(this._deviceRemovedId); for (const device of this.service.devices) this._onDeviceRemoved(this.service, device, false); this.service.destroy(); } // Disconnect any keybindings this._keybindings.destroy(); // Disconnect from any GSettings changes this.settings.disconnect(this._enabledId); this.settings.disconnect(this._panelModeId); this.settings.run_dispose(); // Destroy the PanelMenu.SystemIndicator actors this._item.destroy(); this.menu.destroy(); delete AggregateMenu._gsconnect; super.destroy(); } }); var serviceIndicator = null; function init() { // If installed as a user extension, this will install the Desktop entry, // DBus and systemd service files necessary for DBus activation and // GNotifications. Since there's no uninit()/uninstall() hook for extensions // and they're only used *by* GSConnect, they should be okay to leave. Utils.installService(); // These modify the notification source for GSConnect's GNotifications and // need to be active even when the extension is disabled (eg. lock screen). // Since they *only* affect notifications from GSConnect, it should be okay // to leave them applied. Notification.patchGSConnectNotificationSource(); Notification.patchGtkNotificationDaemon(); // This watches for the service to start and exports a custom clipboard // portal for use on Wayland Clipboard.watchService(); } function enable() { serviceIndicator = new ServiceIndicator(); Notification.patchGtkNotificationSources(); } function disable() { serviceIndicator.destroy(); serviceIndicator = null; Notification.unpatchGtkNotificationSources(); } gnome-shell-extension-gsconnect-50/src/gsconnect-preferences000077500000000000000000000060051421543444100245070ustar00rootroot00000000000000#!/usr/bin/env gjs // -*- mode: js; -*- 'use strict'; imports.gi.versions.Gdk = '3.0'; imports.gi.versions.GdkPixbuf = '2.0'; imports.gi.versions.Gio = '2.0'; imports.gi.versions.GLib = '2.0'; imports.gi.versions.GObject = '2.0'; imports.gi.versions.Gtk = '3.0'; const Gdk = imports.gi.Gdk; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; // Bootstrap function get_datadir() { let m = /@(.+):\d+/.exec((new Error()).stack.split('\n')[1]); return Gio.File.new_for_path(m[1]).get_parent().get_path(); } imports.searchPath.unshift(get_datadir()); imports.config.PACKAGE_DATADIR = imports.searchPath[0]; // Local Imports const Config = imports.config; const Settings = imports.preferences.service; /** * Class representing the GSConnect service daemon. */ const Preferences = GObject.registerClass({ GTypeName: 'GSConnectPreferences', Implements: [Gio.ActionGroup], }, class Preferences extends Gtk.Application { _init() { super._init({ application_id: 'org.gnome.Shell.Extensions.GSConnect.Preferences', resource_base_path: '/org/gnome/Shell/Extensions/GSConnect', }); GLib.set_prgname('gsconnect-preferences'); GLib.set_application_name(_('GSConnect Preferences')); } vfunc_activate() { if (this._window === undefined) { this._window = new Settings.Window({ application: this, }); } this._window.present(); } vfunc_startup() { super.vfunc_startup(); // Init some resources let provider = new Gtk.CssProvider(); provider.load_from_resource(`${Config.APP_PATH}/application.css`); Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ); let actions = [ ['refresh', null], ['connect', GLib.VariantType.new('s')], ]; for (let [name, type] of actions) { let action = new Gio.SimpleAction({ name: name, parameter_type: type, }); this.add_action(action); } } vfunc_activate_action(action_name, parameter) { try { let paramArray = []; if (parameter instanceof GLib.Variant) paramArray[0] = parameter; this.get_dbus_connection().call( 'org.gnome.Shell.Extensions.GSConnect', '/org/gnome/Shell/Extensions/GSConnect', 'org.freedesktop.Application', 'ActivateAction', GLib.Variant.new('(sava{sv})', [action_name, paramArray, {}]), null, Gio.DBusCallFlags.NONE, -1, null, null ); } catch (e) { logError(e); } } }); (new Preferences()).run([imports.system.programInvocationName].concat(ARGV)); gnome-shell-extension-gsconnect-50/src/preferences/000077500000000000000000000000001421543444100225775ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/src/preferences/__init__.js000066400000000000000000000020161421543444100246730ustar00rootroot00000000000000'use strict'; const Gettext = imports.gettext; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Config = imports.config; // Ensure config.js is setup properly const userDir = GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell']); if (Config.PACKAGE_DATADIR.startsWith(userDir)) { Config.IS_USER = true; Config.PACKAGE_LOCALEDIR = `${Config.PACKAGE_DATADIR}/locale`; Config.GSETTINGS_SCHEMA_DIR = `${Config.PACKAGE_DATADIR}/schemas`; } // Init Gettext String.prototype.format = imports.format.format; Gettext.bindtextdomain(Config.APP_ID, Config.PACKAGE_LOCALEDIR); globalThis._ = GLib.dgettext.bind(null, Config.APP_ID); globalThis.ngettext = GLib.dngettext.bind(null, Config.APP_ID); // Init GResources Gio.Resource.load( GLib.build_filenamev([Config.PACKAGE_DATADIR, `${Config.APP_ID}.gresource`]) )._register(); // Init GSchema Config.GSCHEMA = Gio.SettingsSchemaSource.new_from_directory( Config.GSETTINGS_SCHEMA_DIR, Gio.SettingsSchemaSource.get_default(), false ); gnome-shell-extension-gsconnect-50/src/preferences/device.js000066400000000000000000001040331421543444100243750ustar00rootroot00000000000000'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Pango = imports.gi.Pango; const Config = imports.config; const Keybindings = imports.preferences.keybindings; // Build a list of plugins and shortcuts for devices const DEVICE_PLUGINS = []; const DEVICE_SHORTCUTS = {}; for (const name in imports.service.plugins) { const module = imports.service.plugins[name]; if (module.Metadata === undefined) continue; // Plugins DEVICE_PLUGINS.push(name); // Shortcuts (GActions without parameters) for (const [name, action] of Object.entries(module.Metadata.actions)) { if (action.parameter_type === null) DEVICE_SHORTCUTS[name] = [action.icon_name, action.label]; } } /** * A Gtk.ListBoxHeaderFunc for sections that adds separators between each row. * * @param {Gtk.ListBoxRow} row - The current row * @param {Gtk.ListBoxRow} before - The previous row */ function rowSeparators(row, before) { const header = row.get_header(); if (before === null) { if (header !== null) header.destroy(); return; } if (header === null) row.set_header(new Gtk.Separator({visible: true})); } /** * A Gtk.ListBoxSortFunc for SectionRow rows * * @param {Gtk.ListBoxRow} row1 - The first row * @param {Gtk.ListBoxRow} row2 - The second row * @return {number} -1, 0 or 1 */ function titleSortFunc(row1, row2) { if (!row1.title || !row2.title) return 0; return row1.title.localeCompare(row2.title); } /** * A row for a section of settings */ const SectionRow = GObject.registerClass({ GTypeName: 'GSConnectPreferencesSectionRow', Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/preferences-section-row.ui', Children: ['icon-image', 'title-label', 'subtitle-label'], Properties: { 'gicon': GObject.ParamSpec.object( 'gicon', 'GIcon', 'A GIcon for the row', GObject.ParamFlags.READWRITE, Gio.Icon.$gtype ), 'icon-name': GObject.ParamSpec.string( 'icon-name', 'Icon Name', 'An icon name for the row', GObject.ParamFlags.READWRITE, null ), 'subtitle': GObject.ParamSpec.string( 'subtitle', 'Subtitle', 'A subtitle for the row', GObject.ParamFlags.READWRITE, null ), 'title': GObject.ParamSpec.string( 'title', 'Title', 'A title for the row', GObject.ParamFlags.READWRITE, null ), 'widget': GObject.ParamSpec.object( 'widget', 'Widget', 'An action widget for the row', GObject.ParamFlags.READWRITE, Gtk.Widget.$gtype ), }, }, class SectionRow extends Gtk.ListBoxRow { _init(params = {}) { super._init(); // NOTE: we can't pass construct properties to _init() because the // template children are not assigned until after it runs. this.freeze_notify(); Object.assign(this, params); this.thaw_notify(); } get icon_name() { return this.icon_image.icon_name; } set icon_name(icon_name) { if (this.icon_name === icon_name) return; this.icon_image.visible = !!icon_name; this.icon_image.icon_name = icon_name; this.notify('icon-name'); } get gicon() { return this.icon_image.gicon; } set gicon(gicon) { if (this.gicon === gicon) return; this.icon_image.visible = !!gicon; this.icon_image.gicon = gicon; this.notify('gicon'); } get title() { return this.title_label.label; } set title(text) { if (this.title === text) return; this.title_label.visible = !!text; this.title_label.label = text; this.notify('title'); } get subtitle() { return this.subtitle_label.label; } set subtitle(text) { if (this.subtitle === text) return; this.subtitle_label.visible = !!text; this.subtitle_label.label = text; this.notify('subtitle'); } get widget() { if (this._widget === undefined) this._widget = null; return this._widget; } set widget(widget) { if (this.widget === widget) return; if (this.widget instanceof Gtk.Widget) this.widget.destroy(); // Add the widget this._widget = widget; this.get_child().attach(widget, 2, 0, 1, 2); this.notify('widget'); } }); /** * Command Editor Dialog */ const CommandEditor = GObject.registerClass({ GTypeName: 'GSConnectPreferencesCommandEditor', Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/preferences-command-editor.ui', Children: [ 'cancel-button', 'save-button', 'command-entry', 'name-entry', 'command-chooser', ], }, class CommandEditor extends Gtk.Dialog { _onBrowseCommand(entry, icon_pos, event) { this.command_chooser.present(); } _onCommandChosen(dialog, response_id) { if (response_id === Gtk.ResponseType.OK) this.command_entry.text = dialog.get_filename(); dialog.hide(); } _onEntryChanged(entry, pspec) { this.save_button.sensitive = (this.command_name && this.command_line); } get command_line() { return this.command_entry.text; } set command_line(text) { this.command_entry.text = text; } get command_name() { return this.name_entry.text; } set command_name(text) { this.name_entry.text = text; } }); /** * A widget for configuring a remote device. */ var Panel = GObject.registerClass({ GTypeName: 'GSConnectPreferencesDevicePanel', Properties: { 'device': GObject.ParamSpec.object( 'device', 'Device', 'The device being configured', GObject.ParamFlags.READWRITE, GObject.Object.$gtype ), }, Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/preferences-device-panel.ui', Children: [ 'sidebar', 'stack', 'infobar', // Sharing 'sharing', 'sharing-page', 'desktop-list', 'clipboard', 'clipboard-sync', 'mousepad', 'mpris', 'systemvolume', 'share', 'share-list', 'receive-files', 'receive-directory', // Battery 'battery', 'battery-device-label', 'battery-device', 'battery-device-list', 'battery-system-label', 'battery-system', 'battery-system-list', 'battery-custom-notification-value', // RunCommand 'runcommand', 'runcommand-page', 'command-list', 'command-add', // Notifications 'notification', 'notification-page', 'notification-list', 'notification-apps', // Telephony 'telephony', 'telephony-page', 'ringing-list', 'ringing-volume', 'talking-list', 'talking-volume', // Shortcuts 'shortcuts-page', 'shortcuts-actions', 'shortcuts-actions-title', 'shortcuts-actions-list', // Advanced 'advanced-page', 'plugin-list', 'experimental-list', 'device-menu', ], }, class Panel extends Gtk.Grid { _init(device) { super._init({ device: device, }); // GSettings this.settings = new Gio.Settings({ settings_schema: Config.GSCHEMA.lookup( 'org.gnome.Shell.Extensions.GSConnect.Device', true ), path: `/org/gnome/shell/extensions/gsconnect/device/${device.id}/`, }); // Infobar this.device.bind_property( 'paired', this.infobar, 'reveal-child', (GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN) ); this._setupActions(); // Settings Pages this._sharingSettings(); this._batterySettings(); this._runcommandSettings(); this._notificationSettings(); this._telephonySettings(); // -------------------------- this._keybindingSettings(); this._advancedSettings(); // Separate plugins and other settings this.sidebar.set_header_func((row, before) => { if (row.get_name() === 'shortcuts') row.set_header(new Gtk.Separator({visible: true})); }); } get menu() { if (this._menu === undefined) { this._menu = this.device_menu; this._menu.prepend_section(null, this.device.menu); this.insert_action_group('device', this.device.action_group); } return this._menu; } get_incoming_supported(type) { const incoming = this.settings.get_strv('incoming-capabilities'); return incoming.includes(`kdeconnect.${type}`); } get_outgoing_supported(type) { const outgoing = this.settings.get_strv('outgoing-capabilities'); return outgoing.includes(`kdeconnect.${type}`); } _onKeynavFailed(widget, direction) { if (direction === Gtk.DirectionType.UP && widget.prev) widget.prev.child_focus(direction); else if (direction === Gtk.DirectionType.DOWN && widget.next) widget.next.child_focus(direction); return true; } _onSwitcherRowSelected(box, row) { this.stack.set_visible_child_name(row.get_name()); } _onSectionRowActivated(box, row) { if (row.widget !== undefined) row.widget.active = !row.widget.active; } _onToggleRowActivated(box, row) { const widget = row.get_child().get_child_at(1, 0); widget.active = !widget.active; } _onEncryptionInfo() { const dialog = new Gtk.MessageDialog({ buttons: Gtk.ButtonsType.OK, text: _('Encryption Info'), secondary_text: this.device.encryption_info, modal: true, transient_for: this.get_toplevel(), }); dialog.connect('response', (dialog) => dialog.destroy()); dialog.present(); } _deviceAction(action, parameter) { this.action_group.activate_action(action.name, parameter); } dispose() { if (this._commandEditor !== undefined) this._commandEditor.destroy(); // Device signals this.device.action_group.disconnect(this._actionAddedId); this.device.action_group.disconnect(this._actionRemovedId); // GSettings for (const settings of Object.values(this._pluginSettings)) settings.run_dispose(); this.settings.disconnect(this._keybindingsId); this.settings.disconnect(this._disabledPluginsId); this.settings.disconnect(this._supportedPluginsId); this.settings.run_dispose(); } pluginSettings(name) { if (this._pluginSettings === undefined) this._pluginSettings = {}; if (!this._pluginSettings.hasOwnProperty(name)) { const meta = imports.service.plugins[name].Metadata; this._pluginSettings[name] = new Gio.Settings({ settings_schema: Config.GSCHEMA.lookup(meta.id, -1), path: `${this.settings.path}plugin/${name}/`, }); } return this._pluginSettings[name]; } _setupActions() { this.actions = new Gio.SimpleActionGroup(); this.insert_action_group('settings', this.actions); let settings = this.pluginSettings('battery'); this.actions.add_action(settings.create_action('send-statistics')); this.actions.add_action(settings.create_action('low-battery-notification')); this.actions.add_action(settings.create_action('custom-battery-notification')); this.actions.add_action(settings.create_action('custom-battery-notification-value')); this.actions.add_action(settings.create_action('full-battery-notification')); settings = this.pluginSettings('clipboard'); this.actions.add_action(settings.create_action('send-content')); this.actions.add_action(settings.create_action('receive-content')); settings = this.pluginSettings('contacts'); this.actions.add_action(settings.create_action('contacts-source')); settings = this.pluginSettings('mousepad'); this.actions.add_action(settings.create_action('share-control')); settings = this.pluginSettings('mpris'); this.actions.add_action(settings.create_action('share-players')); settings = this.pluginSettings('notification'); this.actions.add_action(settings.create_action('send-notifications')); this.actions.add_action(settings.create_action('send-active')); settings = this.pluginSettings('photo'); this.actions.add_action(settings.create_action('share-camera')); settings = this.pluginSettings('sftp'); this.actions.add_action(settings.create_action('automount')); settings = this.pluginSettings('share'); this.actions.add_action(settings.create_action('receive-files')); settings = this.pluginSettings('sms'); this.actions.add_action(settings.create_action('legacy-sms')); settings = this.pluginSettings('systemvolume'); this.actions.add_action(settings.create_action('share-sinks')); settings = this.pluginSettings('telephony'); this.actions.add_action(settings.create_action('ringing-volume')); this.actions.add_action(settings.create_action('ringing-pause')); this.actions.add_action(settings.create_action('talking-volume')); this.actions.add_action(settings.create_action('talking-pause')); this.actions.add_action(settings.create_action('talking-microphone')); // Pair Actions const encryption_info = new Gio.SimpleAction({name: 'encryption-info'}); encryption_info.connect('activate', this._onEncryptionInfo.bind(this)); this.actions.add_action(encryption_info); const status_pair = new Gio.SimpleAction({name: 'pair'}); status_pair.connect('activate', this._deviceAction.bind(this.device)); this.settings.bind('paired', status_pair, 'enabled', 16); this.actions.add_action(status_pair); const status_unpair = new Gio.SimpleAction({name: 'unpair'}); status_unpair.connect('activate', this._deviceAction.bind(this.device)); this.settings.bind('paired', status_unpair, 'enabled', 0); this.actions.add_action(status_unpair); } /** * Sharing Settings */ _sharingSettings() { // Share Plugin const settings = this.pluginSettings('share'); settings.connect( 'changed::receive-directory', this._onReceiveDirectoryChanged.bind(this) ); this._onReceiveDirectoryChanged(settings, 'receive-directory'); // Visibility this.desktop_list.foreach(row => { const name = row.get_name(); row.visible = this.get_outgoing_supported(`${name}.request`); }); // Separators & Sorting this.desktop_list.set_header_func(rowSeparators); this.desktop_list.set_sort_func((row1, row2) => { row1 = row1.get_child().get_child_at(0, 0); row2 = row2.get_child().get_child_at(0, 0); return row1.label.localeCompare(row2.label); }); this.share_list.set_header_func(rowSeparators); // Scroll with keyboard focus const sharing_box = this.sharing_page.get_child().get_child(); sharing_box.set_focus_vadjustment(this.sharing_page.vadjustment); // Continue focus chain between lists this.desktop_list.next = this.share_list; this.share_list.prev = this.desktop_list; } _onReceiveDirectoryChanged(settings, key) { let receiveDir = settings.get_string(key); if (receiveDir.length === 0) { receiveDir = GLib.get_user_special_dir( GLib.UserDirectory.DIRECTORY_DOWNLOAD ); // Account for some corner cases with a fallback const homeDir = GLib.get_home_dir(); if (!receiveDir || receiveDir === homeDir) receiveDir = GLib.build_filenamev([homeDir, 'Downloads']); settings.set_string(key, receiveDir); } if (this.receive_directory.get_filename() !== receiveDir) this.receive_directory.set_filename(receiveDir); } _onReceiveDirectorySet(button) { const settings = this.pluginSettings('share'); const receiveDir = settings.get_string('receive-directory'); const filename = button.get_filename(); if (filename !== receiveDir) settings.set_string('receive-directory', filename); } /** * Battery Settings */ async _batterySettings() { try { this.battery_device_list.set_header_func(rowSeparators); this.battery_system_list.set_header_func(rowSeparators); const settings = this.pluginSettings('battery'); const oldLevel = settings.get_uint('custom-battery-notification-value'); this.battery_custom_notification_value.set_value(oldLevel); // If the device can't handle statistics we're done if (!this.get_incoming_supported('battery')) { this.battery_system_label.visible = false; this.battery_system.visible = false; return; } // Check UPower for a battery const hasBattery = await new Promise((resolve, reject) => { Gio.DBus.system.call( 'org.freedesktop.UPower', '/org/freedesktop/UPower/devices/DisplayDevice', 'org.freedesktop.DBus.Properties', 'Get', new GLib.Variant('(ss)', [ 'org.freedesktop.UPower.Device', 'IsPresent', ]), null, Gio.DBusCallFlags.NONE, -1, null, (connection, res) => { try { const variant = connection.call_finish(res); const value = variant.deepUnpack()[0]; const isPresent = value.get_boolean(); resolve(isPresent); } catch (e) { resolve(false); } } ); }); this.battery_system_label.visible = hasBattery; this.battery_system.visible = hasBattery; } catch (e) { this.battery_system_label.visible = false; this.battery_system.visible = false; } } _setCustomChargeLevel(spin) { const settings = this.pluginSettings('battery'); settings.set_uint('custom-battery-notification-value', spin.get_value_as_int()); } /** * RunCommand Page */ _runcommandSettings() { // Scroll with keyboard focus const runcommand_box = this.runcommand_page.get_child().get_child(); runcommand_box.set_focus_vadjustment(this.runcommand_page.vadjustment); // Local Command List const settings = this.pluginSettings('runcommand'); this._commands = settings.get_value('command-list').recursiveUnpack(); this.command_list.set_sort_func(this._sortCommands); this.command_list.set_header_func(rowSeparators); for (const uuid of Object.keys(this._commands)) this._insertCommand(uuid); } _sortCommands(row1, row2) { if (!row1.title || !row2.title) return 1; return row1.title.localeCompare(row2.title); } _insertCommand(uuid) { const row = new SectionRow({ title: this._commands[uuid].name, subtitle: this._commands[uuid].command, activatable: false, }); row.set_name(uuid); row.subtitle_label.ellipsize = Pango.EllipsizeMode.MIDDLE; const editButton = new Gtk.Button({ image: new Gtk.Image({ icon_name: 'document-edit-symbolic', pixel_size: 16, visible: true, }), tooltip_text: _('Edit'), valign: Gtk.Align.CENTER, vexpand: true, visible: true, }); editButton.connect('clicked', this._onEditCommand.bind(this)); editButton.get_accessible().set_name(_('Edit')); row.get_child().attach(editButton, 2, 0, 1, 2); const deleteButton = new Gtk.Button({ image: new Gtk.Image({ icon_name: 'edit-delete-symbolic', pixel_size: 16, visible: true, }), tooltip_text: _('Remove'), valign: Gtk.Align.CENTER, vexpand: true, visible: true, }); deleteButton.connect('clicked', this._onDeleteCommand.bind(this)); deleteButton.get_accessible().set_name(_('Remove')); row.get_child().attach(deleteButton, 3, 0, 1, 2); this.command_list.add(row); } _onEditCommand(widget) { if (this._commandEditor === undefined) { this._commandEditor = new CommandEditor({ modal: true, transient_for: this.get_toplevel(), use_header_bar: true, }); this._commandEditor.connect( 'response', this._onSaveCommand.bind(this) ); this._commandEditor.resize(1, 1); } if (widget instanceof Gtk.Button) { const row = widget.get_ancestor(Gtk.ListBoxRow.$gtype); const uuid = row.get_name(); this._commandEditor.uuid = uuid; this._commandEditor.command_name = this._commands[uuid].name; this._commandEditor.command_line = this._commands[uuid].command; } else { this._commandEditor.uuid = GLib.uuid_string_random(); this._commandEditor.command_name = ''; this._commandEditor.command_line = ''; } this._commandEditor.present(); } _storeCommands() { const variant = {}; for (const [uuid, command] of Object.entries(this._commands)) variant[uuid] = new GLib.Variant('a{ss}', command); this.pluginSettings('runcommand').set_value( 'command-list', new GLib.Variant('a{sv}', variant) ); } _onDeleteCommand(button) { const row = button.get_ancestor(Gtk.ListBoxRow.$gtype); delete this._commands[row.get_name()]; row.destroy(); this._storeCommands(); } _onSaveCommand(dialog, response_id) { if (response_id === Gtk.ResponseType.ACCEPT) { this._commands[dialog.uuid] = { name: dialog.command_name, command: dialog.command_line, }; this._storeCommands(); // let row = null; for (const child of this.command_list.get_children()) { if (child.get_name() === dialog.uuid) { row = child; break; } } if (row === null) { this._insertCommand(dialog.uuid); } else { row.set_name(dialog.uuid); row.title = dialog.command_name; row.subtitle = dialog.command_line; } } dialog.hide(); } /** * Notification Settings */ _notificationSettings() { const settings = this.pluginSettings('notification'); settings.bind( 'send-notifications', this.notification_apps, 'sensitive', Gio.SettingsBindFlags.DEFAULT ); // Separators & Sorting this.notification_list.set_header_func(rowSeparators); // Scroll with keyboard focus const notification_box = this.notification_page.get_child().get_child(); notification_box.set_focus_vadjustment(this.notification_page.vadjustment); // Continue focus chain between lists this.notification_list.next = this.notification_apps; this.notification_apps.prev = this.notification_list; this.notification_apps.set_sort_func(titleSortFunc); this.notification_apps.set_header_func(rowSeparators); this._populateApplications(settings); } _toggleNotification(widget) { try { const row = widget.get_ancestor(Gtk.ListBoxRow.$gtype); const settings = this.pluginSettings('notification'); let applications = {}; try { applications = JSON.parse(settings.get_string('applications')); } catch (e) { applications = {}; } applications[row.title].enabled = !applications[row.title].enabled; row.widget.state = applications[row.title].enabled; settings.set_string('applications', JSON.stringify(applications)); } catch (e) { logError(e); } } _populateApplications(settings) { const applications = this._queryApplications(settings); for (const name in applications) { const row = new SectionRow({ gicon: Gio.Icon.new_for_string(applications[name].iconName), title: name, height_request: 48, widget: new Gtk.Switch({ state: applications[name].enabled, margin_start: 12, margin_end: 12, halign: Gtk.Align.END, valign: Gtk.Align.CENTER, vexpand: true, visible: true, }), }); row.widget.connect('notify::active', this._toggleNotification.bind(this)); this.notification_apps.add(row); } } _queryApplications(settings) { let applications = {}; try { applications = JSON.parse(settings.get_string('applications')); } catch (e) { applications = {}; } // Scan applications that statically declare to show notifications const ignoreId = 'org.gnome.Shell.Extensions.GSConnect.desktop'; for (const appInfo of Gio.AppInfo.get_all()) { if (appInfo.get_id() === ignoreId) continue; if (!appInfo.get_boolean('X-GNOME-UsesNotifications')) continue; const appName = appInfo.get_name(); if (appName === null || applications.hasOwnProperty(appName)) continue; let icon = appInfo.get_icon(); icon = (icon) ? icon.to_string() : 'application-x-executable'; applications[appName] = { iconName: icon, enabled: true, }; } settings.set_string('applications', JSON.stringify(applications)); return applications; } /** * Telephony Settings */ _telephonySettings() { // Continue focus chain between lists this.ringing_list.next = this.talking_list; this.talking_list.prev = this.ringing_list; this.ringing_list.set_header_func(rowSeparators); this.talking_list.set_header_func(rowSeparators); } /** * Keyboard Shortcuts */ _keybindingSettings() { // Scroll with keyboard focus const shortcuts_box = this.shortcuts_page.get_child().get_child(); shortcuts_box.set_focus_vadjustment(this.shortcuts_page.vadjustment); // Filter & Sort this.shortcuts_actions_list.set_filter_func(this._filterPluginKeybindings.bind(this)); this.shortcuts_actions_list.set_header_func(rowSeparators); this.shortcuts_actions_list.set_sort_func(titleSortFunc); // Init for (const name in DEVICE_SHORTCUTS) this._addPluginKeybinding(name); this._setPluginKeybindings(); // Watch for GAction and Keybinding changes this._actionAddedId = this.device.action_group.connect( 'action-added', () => this.shortcuts_actions_list.invalidate_filter() ); this._actionRemovedId = this.device.action_group.connect( 'action-removed', () => this.shortcuts_actions_list.invalidate_filter() ); this._keybindingsId = this.settings.connect( 'changed::keybindings', this._setPluginKeybindings.bind(this) ); } _addPluginKeybinding(name) { const [icon_name, label] = DEVICE_SHORTCUTS[name]; const widget = new Gtk.Label({ label: _('Disabled'), visible: true, }); widget.get_style_context().add_class('dim-label'); const row = new SectionRow({ height_request: 48, icon_name: icon_name, title: label, widget: widget, }); row.icon_image.pixel_size = 16; row.action = name; this.shortcuts_actions_list.add(row); } _filterPluginKeybindings(row) { return this.device.action_group.has_action(row.action); } _setPluginKeybindings() { const keybindings = this.settings.get_value('keybindings').deepUnpack(); this.shortcuts_actions_list.foreach(row => { if (keybindings[row.action]) { const accel = Gtk.accelerator_parse(keybindings[row.action]); row.widget.label = Gtk.accelerator_get_label(...accel); } else { row.widget.label = _('Disabled'); } }); } _onResetActionShortcuts(button) { const keybindings = this.settings.get_value('keybindings').deepUnpack(); for (const action in keybindings) { // Don't reset remote command shortcuts if (!action.includes('::')) delete keybindings[action]; } this.settings.set_value( 'keybindings', new GLib.Variant('a{ss}', keybindings) ); } async _onShortcutRowActivated(box, row) { try { const keybindings = this.settings.get_value('keybindings').deepUnpack(); let accel = keybindings[row.action] || null; accel = await Keybindings.getAccelerator(row.title, accel); if (accel) keybindings[row.action] = accel; else delete keybindings[row.action]; this.settings.set_value( 'keybindings', new GLib.Variant('a{ss}', keybindings) ); } catch (e) { logError(e); } } /** * Advanced Page */ _advancedSettings() { // Scroll with keyboard focus const advanced_box = this.advanced_page.get_child().get_child(); advanced_box.set_focus_vadjustment(this.advanced_page.vadjustment); // Sort & Separate this.plugin_list.set_header_func(rowSeparators); this.plugin_list.set_sort_func(titleSortFunc); this.experimental_list.set_header_func(rowSeparators); // Continue focus chain between lists this.plugin_list.next = this.experimental_list; this.experimental_list.prev = this.plugin_list; this._disabledPluginsId = this.settings.connect( 'changed::disabled-plugins', this._onPluginsChanged.bind(this) ); this._supportedPluginsId = this.settings.connect( 'changed::supported-plugins', this._onPluginsChanged.bind(this) ); this._onPluginsChanged(this.settings, null); for (const name of DEVICE_PLUGINS) this._addPlugin(name); } _onPluginsChanged(settings, key) { if (key === 'disabled-plugins' || this._disabledPlugins === undefined) this._disabledPlugins = settings.get_strv('disabled-plugins'); if (key === 'supported-plugins' || this._supportedPlugins === undefined) this._supportedPlugins = settings.get_strv('supported-plugins'); this._enabledPlugins = this._supportedPlugins.filter(name => { return !this._disabledPlugins.includes(name); }); if (key !== null) this._updatePlugins(); } _addPlugin(name) { const plugin = imports.service.plugins[name]; const row = new SectionRow({ height_request: 48, title: plugin.Metadata.label, subtitle: plugin.Metadata.description || '', visible: this._supportedPlugins.includes(name), widget: new Gtk.Switch({ active: this._enabledPlugins.includes(name), valign: Gtk.Align.CENTER, vexpand: true, visible: true, }), }); row.widget.connect('notify::active', this._togglePlugin.bind(this)); row.set_name(name); if (this.hasOwnProperty(name)) this[name].visible = row.widget.active; this.plugin_list.add(row); } _updatePlugins(settings, key) { for (const row of this.plugin_list.get_children()) { const name = row.get_name(); row.visible = this._supportedPlugins.includes(name); row.widget.active = this._enabledPlugins.includes(name); if (this.hasOwnProperty(name)) this[name].visible = row.widget.active; } } _togglePlugin(widget) { try { const name = widget.get_ancestor(Gtk.ListBoxRow.$gtype).get_name(); const index = this._disabledPlugins.indexOf(name); // Either add or remove the plugin from the disabled list if (index > -1) this._disabledPlugins.splice(index, 1); else this._disabledPlugins.push(name); this.settings.set_strv('disabled-plugins', this._disabledPlugins); } catch (e) { logError(e); } } }); gnome-shell-extension-gsconnect-50/src/preferences/keybindings.js000066400000000000000000000215201421543444100254430ustar00rootroot00000000000000'use strict'; const Gdk = imports.gi.Gdk; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; /* * A list of modifier keysyms we ignore */ const _MODIFIERS = [ Gdk.KEY_Alt_L, Gdk.KEY_Alt_R, Gdk.KEY_Caps_Lock, Gdk.KEY_Control_L, Gdk.KEY_Control_R, Gdk.KEY_Meta_L, Gdk.KEY_Meta_R, Gdk.KEY_Num_Lock, Gdk.KEY_Shift_L, Gdk.KEY_Shift_R, Gdk.KEY_Super_L, Gdk.KEY_Super_R, ]; /** * Response enum for ShortcutChooserDialog */ var ResponseType = { CANCEL: Gtk.ResponseType.CANCEL, SET: Gtk.ResponseType.APPLY, UNSET: 2, }; /** * A simplified version of the shortcut editor from GNOME Control Center */ var ShortcutChooserDialog = GObject.registerClass({ GTypeName: 'GSConnectPreferencesShortcutEditor', Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/preferences-shortcut-editor.ui', Children: [ 'cancel-button', 'set-button', 'stack', 'summary-label', 'shortcut-label', 'conflict-label', ], }, class ShortcutChooserDialog extends Gtk.Dialog { _init(params) { super._init({ transient_for: Gio.Application.get_default().get_active_window(), use_header_bar: true, }); this._seat = Gdk.Display.get_default().get_default_seat(); // Current accelerator or %null this.accelerator = params.accelerator; // TRANSLATORS: Summary of a keyboard shortcut function // Example: Enter a new shortcut to change Messaging this.summary = _('Enter a new shortcut to change %s').format( params.summary ); } get accelerator() { return this.shortcut_label.accelerator; } set accelerator(value) { this.shortcut_label.accelerator = value; } get summary() { return this.summary_label.label; } set summary(value) { this.summary_label.label = value; } vfunc_key_press_event(event) { let keyvalLower = Gdk.keyval_to_lower(event.keyval); let realMask = event.state & Gtk.accelerator_get_default_mod_mask(); // TODO: Critical: 'WIDGET_REALIZED_FOR_EVENT (widget, event)' failed if (_MODIFIERS.includes(keyvalLower)) return true; // Normalize Tab if (keyvalLower === Gdk.KEY_ISO_Left_Tab) keyvalLower = Gdk.KEY_Tab; // Put shift back if it changed the case of the key, not otherwise. if (keyvalLower !== event.keyval) realMask |= Gdk.ModifierType.SHIFT_MASK; // HACK: we don't want to use SysRq as a keybinding (but we do want // Alt+Print), so we avoid translation from Alt+Print to SysRq if (keyvalLower === Gdk.KEY_Sys_Req && (realMask & Gdk.ModifierType.MOD1_MASK) !== 0) keyvalLower = Gdk.KEY_Print; // A single Escape press cancels the editing if (realMask === 0 && keyvalLower === Gdk.KEY_Escape) { this.response(ResponseType.CANCEL); return false; } // Backspace disables the current shortcut if (realMask === 0 && keyvalLower === Gdk.KEY_BackSpace) { this.response(ResponseType.UNSET); return false; } // CapsLock isn't supported as a keybinding modifier, so keep it from // confusing us realMask &= ~Gdk.ModifierType.LOCK_MASK; if (keyvalLower !== 0 && realMask !== 0) { this._ungrab(); // Set the accelerator property/label this.accelerator = Gtk.accelerator_name(keyvalLower, realMask); // TRANSLATORS: When a keyboard shortcut is unavailable // Example: [Ctrl]+[S] is already being used this.conflict_label.label = _('%s is already being used').format( Gtk.accelerator_get_label(keyvalLower, realMask) ); // Show Cancel button and switch to confirm/conflict page this.cancel_button.visible = true; this.stack.visible_child_name = 'confirm'; this._check(); } return true; } async _check() { try { const available = await checkAccelerator(this.accelerator); this.set_button.visible = available; this.conflict_label.visible = !available; } catch (e) { logError(e); this.response(ResponseType.CANCEL); } } _grab() { const success = this._seat.grab( this.get_window(), Gdk.SeatCapabilities.KEYBOARD, true, // owner_events null, // cursor null, // event null ); if (success !== Gdk.GrabStatus.SUCCESS) return this.response(ResponseType.CANCEL); if (!this._seat.get_keyboard() && !this._seat.get_pointer()) return this.response(ResponseType.CANCEL); this.grab_add(); } _ungrab() { this._seat.ungrab(); this.grab_remove(); } // Override to use our own ungrab process response(response_id) { this.hide(); this._ungrab(); return super.response(response_id); } // Override with a non-blocking version of Gtk.Dialog.run() run() { this.show(); // Wait a bit before attempting grab GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, () => { this._grab(); return GLib.SOURCE_REMOVE; }); } }); /** * Check the availability of an accelerator using GNOME Shell's DBus interface. * * @param {string} accelerator - An accelerator * @param {number} [modeFlags] - Mode Flags * @param {number} [grabFlags] - Grab Flags * @param {boolean} %true if available, %false on error or unavailable */ async function checkAccelerator(accelerator, modeFlags = 0, grabFlags = 0) { try { let result = false; // Try to grab the accelerator const action = await new Promise((resolve, reject) => { Gio.DBus.session.call( 'org.gnome.Shell', '/org/gnome/Shell', 'org.gnome.Shell', 'GrabAccelerator', new GLib.Variant('(suu)', [accelerator, modeFlags, grabFlags]), null, Gio.DBusCallFlags.NONE, -1, null, (connection, res) => { try { res = connection.call_finish(res); resolve(res.deepUnpack()[0]); } catch (e) { reject(e); } } ); }); // If successful, use the result of ungrabbing as our return if (action !== 0) { result = await new Promise((resolve, reject) => { Gio.DBus.session.call( 'org.gnome.Shell', '/org/gnome/Shell', 'org.gnome.Shell', 'UngrabAccelerator', new GLib.Variant('(u)', [action]), null, Gio.DBusCallFlags.NONE, -1, null, (connection, res) => { try { res = connection.call_finish(res); resolve(res.deepUnpack()[0]); } catch (e) { reject(e); } } ); }); } return result; } catch (e) { logError(e); return false; } } /** * Show a dialog to get a keyboard shortcut from a user. * * @param {string} summary - A description of the keybinding's function * @param {string} accelerator - An accelerator as taken by Gtk.ShortcutLabel * @return {string} An accelerator or %null if it should be unset. */ async function getAccelerator(summary, accelerator = null) { try { const dialog = new ShortcutChooserDialog({ summary: summary, accelerator: accelerator, }); accelerator = await new Promise((resolve, reject) => { dialog.connect('response', (dialog, response) => { switch (response) { case ResponseType.SET: accelerator = dialog.accelerator; break; case ResponseType.UNSET: accelerator = null; break; case ResponseType.CANCEL: // leave the accelerator as passed in break; } dialog.destroy(); resolve(accelerator); }); dialog.run(); }); return accelerator; } catch (e) { logError(e); return accelerator; } } gnome-shell-extension-gsconnect-50/src/preferences/service.js000066400000000000000000000452221421543444100246020ustar00rootroot00000000000000'use strict'; const Gdk = imports.gi.Gdk; const GdkPixbuf = imports.gi.GdkPixbuf; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Config = imports.config; const Device = imports.preferences.device; const Remote = imports.utils.remote; /* * Header for support logs */ const LOG_HEADER = new GLib.Bytes(` GSConnect: ${Config.PACKAGE_VERSION} (${Config.IS_USER ? 'user' : 'system'}) GJS: ${imports.system.version} Session: ${GLib.getenv('XDG_SESSION_TYPE')} OS: ${GLib.get_os_info('PRETTY_NAME')} -------------------------------------------------------------------------------- `); /** * Generate a support log. * * @param {string} time - Start time as a string (24-hour notation) */ async function generateSupportLog(time) { try { const [file, stream] = Gio.File.new_tmp('gsconnect.XXXXXX'); const logFile = stream.get_output_stream(); await new Promise((resolve, reject) => { logFile.write_bytes_async(LOG_HEADER, 0, null, (file, res) => { try { resolve(file.write_bytes_finish(res)); } catch (e) { reject(e); } }); }); // FIXME: BSD??? const proc = new Gio.Subprocess({ flags: (Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_MERGE), argv: ['journalctl', '--no-host', '--since', time], }); proc.init(null); logFile.splice_async( proc.get_stdout_pipe(), Gio.OutputStreamSpliceFlags.CLOSE_TARGET, GLib.PRIORITY_DEFAULT, null, (source, res) => { try { source.splice_finish(res); } catch (e) { logError(e); } } ); await new Promise((resolve, reject) => { proc.wait_check_async(null, (proc, res) => { try { resolve(proc.wait_finish(res)); } catch (e) { reject(e); } }); }); const uri = file.get_uri(); Gio.AppInfo.launch_default_for_uri_async(uri, null, null, null); } catch (e) { logError(e); } } /** * "Connect to..." Dialog */ var ConnectDialog = GObject.registerClass({ GTypeName: 'GSConnectConnectDialog', Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/connect-dialog.ui', Children: [ 'cancel-button', 'connect-button', 'lan-grid', 'lan-ip', 'lan-port', ], }, class ConnectDialog extends Gtk.Dialog { _init(params = {}) { super._init(Object.assign({ use_header_bar: true, }, params)); } vfunc_response(response_id) { if (response_id === Gtk.ResponseType.OK) { try { let address; // Lan host/port entered if (this.lan_ip.text) { const host = this.lan_ip.text; const port = this.lan_port.value; address = GLib.Variant.new_string(`lan://${host}:${port}`); } else { return false; } this.application.activate_action('connect', address); } catch (e) { logError(e); } } this.destroy(); return false; } }); function rowSeparators(row, before) { const header = row.get_header(); if (before === null) { if (header !== null) header.destroy(); return; } if (header === null) row.set_header(new Gtk.Separator({visible: true})); } var Window = GObject.registerClass({ GTypeName: 'GSConnectPreferencesWindow', Properties: { 'display-mode': GObject.ParamSpec.string( 'display-mode', 'Display Mode', 'Display devices in either the Panel or User Menu', GObject.ParamFlags.READWRITE, null ), }, Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/preferences-window.ui', Children: [ // HeaderBar 'headerbar', 'infobar', 'stack', 'service-menu', 'service-edit', 'refresh-button', 'device-menu', 'prev-button', // Popover 'rename-popover', 'rename', 'rename-label', 'rename-entry', 'rename-submit', // Focus Box 'service-window', 'service-box', // Device List 'device-list', 'device-list-spinner', 'device-list-placeholder', ], }, class PreferencesWindow extends Gtk.ApplicationWindow { _init(params = {}) { super._init(params); // Service Settings this.settings = new Gio.Settings({ settings_schema: Config.GSCHEMA.lookup( 'org.gnome.Shell.Extensions.GSConnect', true ), }); // Service Proxy this.service = new Remote.Service(); this._deviceAddedId = this.service.connect( 'device-added', this._onDeviceAdded.bind(this) ); this._deviceRemovedId = this.service.connect( 'device-removed', this._onDeviceRemoved.bind(this) ); this._serviceChangedId = this.service.connect( 'notify::active', this._onServiceChanged.bind(this) ); // HeaderBar (Service Name) this.headerbar.title = this.settings.get_string('name'); this.rename_entry.text = this.headerbar.title; // Scroll with keyboard focus this.service_box.set_focus_vadjustment(this.service_window.vadjustment); // Device List this.device_list.set_header_func(rowSeparators); // Discoverable InfoBar this.settings.bind( 'discoverable', this.infobar, 'reveal-child', Gio.SettingsBindFlags.INVERT_BOOLEAN ); this.add_action(this.settings.create_action('discoverable')); // Application Menu this._initMenu(); // Broadcast automatically every 5 seconds if there are no devices yet this._refreshSource = GLib.timeout_add_seconds( GLib.PRIORITY_DEFAULT, 5, this._refresh.bind(this) ); // Restore window size/maximized/position this._restoreGeometry(); // Prime the service this._initService(); } get display_mode() { if (this.settings.get_boolean('show-indicators')) return 'panel'; return 'user-menu'; } set display_mode(mode) { this.settings.set_boolean('show-indicators', (mode === 'panel')); } vfunc_delete_event(event) { if (this.service) { this.service.disconnect(this._deviceAddedId); this.service.disconnect(this._deviceRemovedId); this.service.disconnect(this._serviceChangedId); this.service.destroy(); this.service = null; } this._saveGeometry(); GLib.source_remove(this._refreshSource); return false; } async _initService() { try { this.refresh_button.grab_focus(); this._onServiceChanged(this.service, null); await this.service.reload(); } catch (e) { logError(e, 'GSConnect'); } } _initMenu() { // Panel/User Menu mode const displayMode = new Gio.PropertyAction({ name: 'display-mode', property_name: 'display-mode', object: this, }); this.add_action(displayMode); // About Dialog const aboutDialog = new Gio.SimpleAction({name: 'about'}); aboutDialog.connect('activate', this._aboutDialog.bind(this)); this.add_action(aboutDialog); // "Connect to..." Dialog const connectDialog = new Gio.SimpleAction({name: 'connect'}); connectDialog.connect('activate', this._connectDialog.bind(this)); this.add_action(connectDialog); // "Generate Support Log" GAction const generateSupportLog = new Gio.SimpleAction({name: 'support-log'}); generateSupportLog.connect('activate', this._generateSupportLog.bind(this)); this.add_action(generateSupportLog); // "Help" GAction const help = new Gio.SimpleAction({name: 'help'}); help.connect('activate', this._help); this.add_action(help); } _refresh() { if (this.service.active && this.device_list.get_children().length < 1) { this.device_list_spinner.active = true; this.service.activate_action('refresh', null); } else { this.device_list_spinner.active = false; } return GLib.SOURCE_CONTINUE; } /* * Window State */ _restoreGeometry() { this._windowState = new Gio.Settings({ settings_schema: Config.GSCHEMA.lookup( 'org.gnome.Shell.Extensions.GSConnect.WindowState', true ), path: '/org/gnome/shell/extensions/gsconnect/preferences/', }); // Size const [width, height] = this._windowState.get_value('window-size').deepUnpack(); if (width && height) this.set_default_size(width, height); // Maximized State if (this._windowState.get_boolean('window-maximized')) this.maximize(); } _saveGeometry() { const state = this.get_window().get_state(); // Maximized State const maximized = (state & Gdk.WindowState.MAXIMIZED); this._windowState.set_boolean('window-maximized', maximized); // Leave the size at the value before maximizing if (maximized || (state & Gdk.WindowState.FULLSCREEN)) return; // Size const size = this.get_size(); this._windowState.set_value('window-size', new GLib.Variant('(ii)', size)); } /** * About Dialog */ _aboutDialog() { if (this._about === undefined) { this._about = new Gtk.AboutDialog({ application: Gio.Application.get_default(), authors: [ 'Andy Holmes ', 'Bertrand Lacoste ', 'Frank Dana ', ], comments: _('A complete KDE Connect implementation for GNOME'), logo: GdkPixbuf.Pixbuf.new_from_resource_at_scale( '/org/gnome/Shell/Extensions/GSConnect/icons/org.gnome.Shell.Extensions.GSConnect.svg', 128, 128, true ), program_name: 'GSConnect', // TRANSLATORS: eg. 'Translator Name ' translator_credits: _('translator-credits'), version: Config.PACKAGE_VERSION.toString(), website: Config.PACKAGE_URL, license_type: Gtk.License.GPL_2_0, modal: true, transient_for: this, }); // Persist this._about.connect('response', (dialog) => dialog.hide_on_delete()); this._about.connect('delete-event', (dialog) => dialog.hide_on_delete()); } this._about.present(); } /** * Connect to..." Dialog */ _connectDialog() { new ConnectDialog({ application: Gio.Application.get_default(), modal: true, transient_for: this, }); } /* * "Generate Support Log" GAction */ _generateSupportLog() { const dialog = new Gtk.MessageDialog({ text: _('Generate Support Log'), secondary_text: _('Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log.'), }); dialog.add_button(_('Cancel'), Gtk.ResponseType.CANCEL); dialog.add_button(_('Review Log'), Gtk.ResponseType.OK); // Enable debug logging and mark the current time this.settings.set_boolean('debug', true); const now = GLib.DateTime.new_now_local().format('%R'); dialog.connect('response', (dialog, response_id) => { // Disable debug logging and destroy the dialog this.settings.set_boolean('debug', false); dialog.destroy(); // Only generate a log if instructed if (response_id === Gtk.ResponseType.OK) generateSupportLog(now); }); dialog.show_all(); } /* * "Help" GAction */ _help(action, parameter) { const uri = `${Config.PACKAGE_URL}/wiki/Help`; Gio.AppInfo.launch_default_for_uri_async(uri, null, null, null); } /* * HeaderBar Callbacks */ _onPrevious(button, event) { // HeaderBar (Service) this.prev_button.visible = false; this.device_menu.visible = false; this.refresh_button.visible = true; this.service_edit.visible = true; this.service_menu.visible = true; this.headerbar.title = this.settings.get_string('name'); this.headerbar.subtitle = null; // Panel this.stack.visible_child_name = 'service'; this._setDeviceMenu(); } _onEditServiceName(button, event) { this.rename_entry.text = this.headerbar.title; this.rename_entry.has_focus = true; } _onSetServiceName(widget) { if (this.rename_entry.text.length) { this.headerbar.title = this.rename_entry.text; this.settings.set_string('name', this.rename_entry.text); } this.service_edit.active = false; this.service_edit.grab_focus(); } /* * Context Switcher */ _getTypeLabel(device) { switch (device.type) { case 'laptop': return _('Laptop'); case 'phone': return _('Smartphone'); case 'tablet': return _('Tablet'); case 'tv': return _('Television'); default: return _('Desktop'); } } _setDeviceMenu(panel = null) { this.device_menu.insert_action_group('device', null); this.device_menu.insert_action_group('settings', null); this.device_menu.set_menu_model(null); if (panel === null) return; this.device_menu.insert_action_group('device', panel.device.action_group); this.device_menu.insert_action_group('settings', panel.actions); this.device_menu.set_menu_model(panel.menu); } _onDeviceChanged(statusLabel, device, pspec) { switch (false) { case device.paired: statusLabel.label = _('Unpaired'); break; case device.connected: statusLabel.label = _('Disconnected'); break; default: statusLabel.label = _('Connected'); } } _createDeviceRow(device) { const row = new Gtk.ListBoxRow({ height_request: 52, selectable: false, visible: true, }); row.set_name(device.id); const grid = new Gtk.Grid({ column_spacing: 12, margin_left: 20, margin_right: 20, margin_bottom: 8, margin_top: 8, visible: true, }); row.add(grid); const icon = new Gtk.Image({ gicon: new Gio.ThemedIcon({name: device.icon_name}), icon_size: Gtk.IconSize.BUTTON, visible: true, }); grid.attach(icon, 0, 0, 1, 1); const title = new Gtk.Label({ halign: Gtk.Align.START, hexpand: true, valign: Gtk.Align.CENTER, vexpand: true, visible: true, }); grid.attach(title, 1, 0, 1, 1); const status = new Gtk.Label({ halign: Gtk.Align.END, hexpand: true, valign: Gtk.Align.CENTER, vexpand: true, visible: true, }); grid.attach(status, 2, 0, 1, 1); // Keep name up to date device.bind_property( 'name', title, 'label', GObject.BindingFlags.SYNC_CREATE ); // Keep status up to date device.connect( 'notify::connected', this._onDeviceChanged.bind(null, status) ); device.connect( 'notify::paired', this._onDeviceChanged.bind(null, status) ); this._onDeviceChanged(status, device, null); return row; } _onDeviceAdded(service, device) { try { if (!this.stack.get_child_by_name(device.id)) { // Add the device preferences const prefs = new Device.Panel(device); this.stack.add_titled(prefs, device.id, device.name); // Add a row to the device list prefs.row = this._createDeviceRow(device); this.device_list.add(prefs.row); } } catch (e) { logError(e); } } _onDeviceRemoved(service, device) { try { const prefs = this.stack.get_child_by_name(device.id); if (prefs === null) return; if (prefs === this.stack.get_visible_child()) this._onPrevious(); prefs.row.destroy(); prefs.row = null; prefs.dispose(); prefs.destroy(); } catch (e) { logError(e); } } _onDeviceSelected(box, row) { try { if (row === null) return this._onPrevious(); // Transition the panel const name = row.get_name(); const prefs = this.stack.get_child_by_name(name); this.stack.visible_child = prefs; this._setDeviceMenu(prefs); // HeaderBar (Device) this.refresh_button.visible = false; this.service_edit.visible = false; this.service_menu.visible = false; this.prev_button.visible = true; this.device_menu.visible = true; this.headerbar.title = prefs.device.name; this.headerbar.subtitle = this._getTypeLabel(prefs.device); } catch (e) { logError(e); } } _onServiceChanged(service, pspec) { if (this.service.active) this.device_list_placeholder.label = _('Searching for devices…'); else this.device_list_placeholder.label = _('Waiting for service…'); } }); gnome-shell-extension-gsconnect-50/src/prefs.js000066400000000000000000000011441421543444100217530ustar00rootroot00000000000000'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Gtk = imports.gi.Gtk; // Bootstrap const Extension = imports.misc.extensionUtils.getCurrentExtension(); const Utils = Extension.imports.shell.utils; function init() { Utils.installService(); } function buildPrefsWidget() { // Destroy the window once the mainloop starts const widget = new Gtk.Box(); GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { widget.get_root().destroy(); return false; }); Gio.Subprocess.new([`${Extension.path}/gsconnect-preferences`], 0); return widget; } gnome-shell-extension-gsconnect-50/src/service/000077500000000000000000000000001421543444100217365ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/src/service/__init__.js000066400000000000000000000276471421543444100240530ustar00rootroot00000000000000'use strict'; const ByteArray = imports.byteArray; const Gettext = imports.gettext; const Gio = imports.gi.Gio; const GIRepository = imports.gi.GIRepository; const GLib = imports.gi.GLib; const Config = imports.config; // User Directories Config.CACHEDIR = GLib.build_filenamev([GLib.get_user_cache_dir(), 'gsconnect']); Config.CONFIGDIR = GLib.build_filenamev([GLib.get_user_config_dir(), 'gsconnect']); Config.RUNTIMEDIR = GLib.build_filenamev([GLib.get_user_runtime_dir(), 'gsconnect']); // Ensure config.js is setup properly const userDir = GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell']); if (Config.PACKAGE_DATADIR.startsWith(userDir)) { Config.IS_USER = true; Config.GSETTINGS_SCHEMA_DIR = `${Config.PACKAGE_DATADIR}/schemas`; Config.PACKAGE_LOCALEDIR = `${Config.PACKAGE_DATADIR}/locale`; // Infer libdir by assuming gnome-shell shares a common prefix with gjs; // assume the parent directory if it's not there let libdir = GIRepository.Repository.get_search_path().find(path => { return path.endsWith('/gjs/girepository-1.0'); }).replace('/gjs/girepository-1.0', ''); const gsdir = GLib.build_filenamev([libdir, 'gnome-shell']); if (!GLib.file_test(gsdir, GLib.FileTest.IS_DIR)) { const currentDir = `/${GLib.path_get_basename(libdir)}`; libdir = libdir.replace(currentDir, ''); } Config.GNOME_SHELL_LIBDIR = libdir; } // Init Gettext String.prototype.format = imports.format.format; Gettext.bindtextdomain(Config.APP_ID, Config.PACKAGE_LOCALEDIR); globalThis._ = GLib.dgettext.bind(null, Config.APP_ID); globalThis.ngettext = GLib.dngettext.bind(null, Config.APP_ID); // Init GResources Gio.Resource.load( GLib.build_filenamev([Config.PACKAGE_DATADIR, `${Config.APP_ID}.gresource`]) )._register(); // Init GSchema Config.GSCHEMA = Gio.SettingsSchemaSource.new_from_directory( Config.GSETTINGS_SCHEMA_DIR, Gio.SettingsSchemaSource.get_default(), false ); // Load DBus interfaces Config.DBUS = (() => { const bytes = Gio.resources_lookup_data( GLib.build_filenamev([Config.APP_PATH, `${Config.APP_ID}.xml`]), Gio.ResourceLookupFlags.NONE ); const xml = ByteArray.toString(bytes.toArray()); const dbus = Gio.DBusNodeInfo.new_for_xml(xml); dbus.nodes.forEach(info => info.cache_build()); return dbus; })(); // Init User Directories for (const path of [Config.CACHEDIR, Config.CONFIGDIR, Config.RUNTIMEDIR]) GLib.mkdir_with_parents(path, 0o755); /** * Check if we're in a Wayland session (mostly for input synthesis) * https://wiki.gnome.org/Accessibility/Wayland#Bugs.2FIssues_We_Must_Address */ globalThis.HAVE_REMOTEINPUT = GLib.getenv('GDMSESSION') !== 'ubuntu-wayland'; globalThis.HAVE_WAYLAND = GLib.getenv('XDG_SESSION_TYPE') === 'wayland'; /** * A custom debug function that logs at LEVEL_MESSAGE to avoid the need for env * variables to be set. * * @param {Error|string} message - A string or Error to log * @param {string} [prefix] - An optional prefix for the warning */ const _debugCallerMatch = new RegExp(/([^@]*)@([^:]*):([^:]*)/); // eslint-disable-next-line func-style const _debugFunc = function (error, prefix = null) { let caller, message; if (error.stack) { caller = error.stack.split('\n')[0]; message = `${error.message}\n${error.stack}`; } else { caller = (new Error()).stack.split('\n')[1]; message = JSON.stringify(error, null, 2); } if (prefix) message = `${prefix}: ${message}`; const [, func, file, line] = _debugCallerMatch.exec(caller); const script = file.replace(Config.PACKAGE_DATADIR, ''); GLib.log_structured('GSConnect', GLib.LogLevelFlags.LEVEL_MESSAGE, { 'MESSAGE': `[${script}:${func}:${line}]: ${message}`, 'SYSLOG_IDENTIFIER': 'org.gnome.Shell.Extensions.GSConnect', 'CODE_FILE': file, 'CODE_FUNC': func, 'CODE_LINE': line, }); }; // Swap the function out for a no-op anonymous function for speed const settings = new Gio.Settings({ settings_schema: Config.GSCHEMA.lookup(Config.APP_ID, true), }); settings.connect('changed::debug', (settings, key) => { globalThis.debug = settings.get_boolean(key) ? _debugFunc : () => {}; }); if (settings.get_boolean('debug')) globalThis.debug = _debugFunc; else globalThis.debug = () => {}; /** * A simple (for now) pre-comparison sanitizer for phone numbers * See: https://github.com/KDE/kdeconnect-kde/blob/master/smsapp/conversationlistmodel.cpp#L200-L210 * * @return {string} Return the string stripped of leading 0, and ' ()-+' */ String.prototype.toPhoneNumber = function () { const strippedNumber = this.replace(/^0*|[ ()+-]/g, ''); if (strippedNumber.length) return strippedNumber; return this; }; /** * A simple equality check for phone numbers based on `toPhoneNumber()` * * @param {string} number - A phone number string to compare * @return {boolean} If `this` and @number are equivalent phone numbers */ String.prototype.equalsPhoneNumber = function (number) { const a = this.toPhoneNumber(); const b = number.toPhoneNumber(); return (a.length && b.length && (a.endsWith(b) || b.endsWith(a))); }; /** * An implementation of `rm -rf` in Gio * * @param {Gio.File|string} file - a GFile or filepath */ Gio.File.rm_rf = function (file) { try { if (typeof file === 'string') file = Gio.File.new_for_path(file); try { const iter = file.enumerate_children( 'standard::name', Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null ); let info; while ((info = iter.next_file(null))) Gio.File.rm_rf(iter.get_child(info)); iter.close(null); } catch (e) { // Silence errors } file.delete(null); } catch (e) { // Silence errors } }; /** * Extend GLib.Variant with a static method to recursively pack a variant * * @param {*} [obj] - May be a GLib.Variant, Array, standard Object or literal. * @return {GLib.Variant} The resulting GVariant */ function _full_pack(obj) { let packed; const type = typeof obj; switch (true) { case (obj instanceof GLib.Variant): return obj; case (type === 'string'): return GLib.Variant.new('s', obj); case (type === 'number'): return GLib.Variant.new('d', obj); case (type === 'boolean'): return GLib.Variant.new('b', obj); case (obj instanceof Uint8Array): return GLib.Variant.new('ay', obj); case (obj === null): return GLib.Variant.new('mv', null); case (typeof obj.map === 'function'): return GLib.Variant.new( 'av', obj.filter(e => e !== undefined).map(e => _full_pack(e)) ); case (obj instanceof Gio.Icon): return obj.serialize(); case (type === 'object'): packed = {}; for (const [key, val] of Object.entries(obj)) { if (val !== undefined) packed[key] = _full_pack(val); } return GLib.Variant.new('a{sv}', packed); default: throw Error(`Unsupported type '${type}': ${obj}`); } } GLib.Variant.full_pack = _full_pack; /** * Extend GLib.Variant with a method to recursively deepUnpack() a variant * * @param {*} [obj] - May be a GLib.Variant, Array, standard Object or literal. * @return {*} The resulting object */ function _full_unpack(obj) { obj = (obj === undefined) ? this : obj; const unpacked = {}; switch (true) { case (obj === null): return obj; case (obj instanceof GLib.Variant): return _full_unpack(obj.deepUnpack()); case (obj instanceof Uint8Array): return obj; case (typeof obj.map === 'function'): return obj.map(e => _full_unpack(e)); case (typeof obj === 'object'): for (const [key, value] of Object.entries(obj)) { // Try to detect and deserialize GIcons try { if (key === 'icon' && value.get_type_string() === '(sv)') unpacked[key] = Gio.Icon.deserialize(value); else unpacked[key] = _full_unpack(value); } catch (e) { unpacked[key] = _full_unpack(value); } } return unpacked; default: return obj; } } GLib.Variant.prototype.full_unpack = _full_unpack; /** * Creates a GTlsCertificate from the PEM-encoded data in @cert_path and * @key_path. If either are missing a new pair will be generated. * * Additionally, the private key will be added using ssh-add to allow sftp * connections using Gio. * * See: https://github.com/KDE/kdeconnect-kde/blob/master/core/kdeconnectconfig.cpp#L119 * * @param {string} certPath - Absolute path to a x509 certificate in PEM format * @param {string} keyPath - Absolute path to a private key in PEM format * @param {string} commonName - A unique common name for the certificate * @return {Gio.TlsCertificate} A TLS certificate */ Gio.TlsCertificate.new_for_paths = function (certPath, keyPath, commonName = null) { // Check if the certificate/key pair already exists const certExists = GLib.file_test(certPath, GLib.FileTest.EXISTS); const keyExists = GLib.file_test(keyPath, GLib.FileTest.EXISTS); // Create a new certificate and private key if necessary if (!certExists || !keyExists) { // If we weren't passed a common name, generate a random one if (!commonName) commonName = GLib.uuid_string_random(); const proc = new Gio.Subprocess({ argv: [ Config.OPENSSL_PATH, 'req', '-new', '-x509', '-sha256', '-out', certPath, '-newkey', 'rsa:4096', '-nodes', '-keyout', keyPath, '-days', '3650', '-subj', `/O=andyholmes.github.io/OU=GSConnect/CN=${commonName}`, ], flags: (Gio.SubprocessFlags.STDOUT_SILENCE | Gio.SubprocessFlags.STDERR_SILENCE), }); proc.init(null); proc.wait_check(null); } return Gio.TlsCertificate.new_from_files(certPath, keyPath); }; Object.defineProperties(Gio.TlsCertificate.prototype, { /** * Compute a SHA256 fingerprint of the certificate. * See: https://gitlab.gnome.org/GNOME/glib/issues/1290 * * @return {string} A SHA256 fingerprint of the certificate. */ 'sha256': { value: function () { if (!this.__fingerprint) { const proc = new Gio.Subprocess({ argv: [Config.OPENSSL_PATH, 'x509', '-noout', '-fingerprint', '-sha256', '-inform', 'pem'], flags: Gio.SubprocessFlags.STDIN_PIPE | Gio.SubprocessFlags.STDOUT_PIPE, }); proc.init(null); const stdout = proc.communicate_utf8(this.certificate_pem, null)[1]; this.__fingerprint = /[a-zA-Z0-9:]{95}/.exec(stdout)[0]; } return this.__fingerprint; }, enumerable: false, }, /** * The common name of the certificate. */ 'common_name': { get: function () { if (!this.__common_name) { const proc = new Gio.Subprocess({ argv: [Config.OPENSSL_PATH, 'x509', '-noout', '-subject', '-inform', 'pem'], flags: Gio.SubprocessFlags.STDIN_PIPE | Gio.SubprocessFlags.STDOUT_PIPE, }); proc.init(null); const stdout = proc.communicate_utf8(this.certificate_pem, null)[1]; this.__common_name = /(?:cn|CN) ?= ?([^,\n]*)/.exec(stdout)[1]; } return this.__common_name; }, enumerable: true, }, }); gnome-shell-extension-gsconnect-50/src/service/backends/000077500000000000000000000000001421543444100235105ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/src/service/backends/lan.js000066400000000000000000000726621421543444100246350ustar00rootroot00000000000000'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Config = imports.config; const Core = imports.service.core; /** * TCP Port Constants */ const DEFAULT_PORT = 1716; const TRANSFER_MIN = 1739; const TRANSFER_MAX = 1764; /* * One-time check for Linux/FreeBSD socket options */ var _LINUX_SOCKETS = true; try { // This should throw on FreeBSD Gio.Socket.new( Gio.SocketFamily.IPV4, Gio.SocketType.STREAM, Gio.SocketProtocol.TCP ).get_option(6, 5); } catch (e) { _LINUX_SOCKETS = false; } /** * Configure a socket connection for the KDE Connect protocol. * * @param {Gio.SocketConnection} connection - The connection to configure */ function _configureSocket(connection) { try { if (_LINUX_SOCKETS) { connection.socket.set_option(6, 4, 10); // TCP_KEEPIDLE connection.socket.set_option(6, 5, 5); // TCP_KEEPINTVL connection.socket.set_option(6, 6, 3); // TCP_KEEPCNT // FreeBSD constants // https://github.com/freebsd/freebsd/blob/master/sys/netinet/tcp.h#L159 } else { connection.socket.set_option(6, 256, 10); // TCP_KEEPIDLE connection.socket.set_option(6, 512, 5); // TCP_KEEPINTVL connection.socket.set_option(6, 1024, 3); // TCP_KEEPCNT } // Do this last because an error setting the keepalive options would // result in a socket that never times out connection.socket.set_keepalive(true); } catch (e) { debug(e, 'Configuring Socket'); } } /** * Lan.ChannelService consists of two parts: * * The TCP Listener listens on a port and constructs a Channel object from the * incoming Gio.TcpConnection. * * The UDP Listener listens on a port for incoming JSON identity packets which * include the TCP port, while the IP address is taken from the UDP packet * itself. We respond by opening a TCP connection to that address. */ var ChannelService = GObject.registerClass({ GTypeName: 'GSConnectLanChannelService', Properties: { 'certificate': GObject.ParamSpec.object( 'certificate', 'Certificate', 'The TLS certificate', GObject.ParamFlags.READWRITE, Gio.TlsCertificate.$gtype ), 'port': GObject.ParamSpec.uint( 'port', 'Port', 'The port used by the service', GObject.ParamFlags.READWRITE, 0, GLib.MAXUINT16, DEFAULT_PORT ), }, }, class LanChannelService extends Core.ChannelService { _init(params = {}) { super._init(params); // Track hosts we identify to directly, allowing them to ignore the // discoverable state of the service. this._allowed = new Set(); // this._tcp = null; this._udp4 = null; this._udp6 = null; // Monitor network status this._networkMonitor = Gio.NetworkMonitor.get_default(); this._networkAvailable = false; this._networkChangedId = 0; } get certificate() { if (this._certificate === undefined) this._certificate = null; return this._certificate; } set certificate(certificate) { if (this.certificate === certificate) return; this._certificate = certificate; this.notify('certificate'); } get channels() { if (this._channels === undefined) this._channels = new Map(); return this._channels; } get port() { if (this._port === undefined) this._port = DEFAULT_PORT; return this._port; } set port(port) { if (this.port === port) return; this._port = port; this.notify('port'); } _onNetworkChanged(monitor, network_available) { if (this._networkAvailable === network_available) return; this._networkAvailable = network_available; this.broadcast(); } _initCertificate() { if (GLib.find_program_in_path(Config.OPENSSL_PATH) === null) { const error = new Error(); error.name = _('OpenSSL not found'); error.url = `${Config.PACKAGE_URL}/wiki/Error#openssl-not-found`; throw error; } const certPath = GLib.build_filenamev([ Config.CONFIGDIR, 'certificate.pem', ]); const keyPath = GLib.build_filenamev([ Config.CONFIGDIR, 'private.pem', ]); // Ensure a certificate exists with our id as the common name this._certificate = Gio.TlsCertificate.new_for_paths(certPath, keyPath, this.id); // If the service ID doesn't match the common name, this is probably a // certificate from an older version and we should amend ours to match if (this.id !== this._certificate.common_name) this._id = this._certificate.common_name; } _initTcpListener() { try { this._tcp = new Gio.SocketService(); this._tcp.add_inet_port(this.port, null); this._tcp.connect('incoming', this._onIncomingChannel.bind(this)); } catch (e) { this._tcp = null; throw e; } } async _onIncomingChannel(listener, connection) { try { const host = connection.get_remote_address().address.to_string(); // Create a channel const channel = new Channel({ backend: this, certificate: this.certificate, host: host, port: this.port, }); // Accept the connection await channel.accept(connection); channel.identity.body.tcpHost = channel.host; channel.identity.body.tcpPort = this.port; channel.allowed = this._allowed.has(host); this.channel(channel); } catch (e) { debug(e); } } _initUdpListener() { // Default broadcast address this._udp_address = Gio.InetSocketAddress.new_from_string( '255.255.255.255', this.port ); try { this._udp6 = Gio.Socket.new( Gio.SocketFamily.IPV6, Gio.SocketType.DATAGRAM, Gio.SocketProtocol.UDP ); this._udp6.set_broadcast(true); // Bind the socket const inetAddr = Gio.InetAddress.new_any(Gio.SocketFamily.IPV6); const sockAddr = Gio.InetSocketAddress.new(inetAddr, this.port); this._udp6.bind(sockAddr, false); // Input stream this._udp6_stream = new Gio.DataInputStream({ base_stream: new Gio.UnixInputStream({ fd: this._udp6.fd, close_fd: false, }), }); // Watch socket for incoming packets this._udp6_source = this._udp6.create_source(GLib.IOCondition.IN, null); this._udp6_source.set_callback(this._onIncomingIdentity.bind(this, this._udp6)); this._udp6_source.attach(null); } catch (e) { this._udp6 = null; } // Our IPv6 socket also supports IPv4; we're all done if (this._udp6 && this._udp6.speaks_ipv4()) { this._udp4 = null; return; } try { this._udp4 = Gio.Socket.new( Gio.SocketFamily.IPV4, Gio.SocketType.DATAGRAM, Gio.SocketProtocol.UDP ); this._udp4.set_broadcast(true); // Bind the socket const inetAddr = Gio.InetAddress.new_any(Gio.SocketFamily.IPV4); const sockAddr = Gio.InetSocketAddress.new(inetAddr, this.port); this._udp4.bind(sockAddr, false); // Input stream this._udp4_stream = new Gio.DataInputStream({ base_stream: new Gio.UnixInputStream({ fd: this._udp4.fd, close_fd: false, }), }); // Watch input socket for incoming packets this._udp4_source = this._udp4.create_source(GLib.IOCondition.IN, null); this._udp4_source.set_callback(this._onIncomingIdentity.bind(this, this._udp4)); this._udp4_source.attach(null); } catch (e) { this._udp4 = null; // We failed to get either an IPv4 or IPv6 socket to bind if (this._udp6 === null) throw e; } } _onIncomingIdentity(socket) { let host, data, packet; // Try to peek the remote address try { host = socket.receive_message( [], Gio.SocketMsgFlags.PEEK, null )[1].address.to_string(); } catch (e) { logError(e); } // Whether or not we peeked the address, we need to read the packet try { if (socket === this._udp6) data = this._udp6_stream.read_line_utf8(null)[0]; else data = this._udp4_stream.read_line_utf8(null)[0]; // Discard the packet if we failed to peek the address if (host === undefined) return; packet = new Core.Packet(data); packet.body.tcpHost = host; this._onIdentity(packet); } catch (e) { logError(e); } return GLib.SOURCE_CONTINUE; } async _onIdentity(packet) { try { // Bail if the deviceId is missing if (!packet.body.hasOwnProperty('deviceId')) return; // Silently ignore our own broadcasts if (packet.body.deviceId === this.identity.body.deviceId) return; debug(packet); // Create a new channel const channel = new Channel({ backend: this, certificate: this.certificate, host: packet.body.tcpHost, port: packet.body.tcpPort, identity: packet, }); // Check if channel is already open with this address if (this.channels.has(channel.address)) return; this._channels.set(channel.address, channel); // Open a TCP connection const connection = await new Promise((resolve, reject) => { const address = Gio.InetSocketAddress.new_from_string( packet.body.tcpHost, packet.body.tcpPort ); const client = new Gio.SocketClient({enable_proxy: false}); client.connect_async(address, null, (client, res) => { try { resolve(client.connect_finish(res)); } catch (e) { reject(e); } }); }); // Connect the channel and attach it to the device on success await channel.open(connection); this.channel(channel); } catch (e) { logError(e); } } /** * Broadcast an identity packet * * If @address is not %null it may specify an IPv4 or IPv6 address to send * the identity packet directly to, otherwise it will be broadcast to the * default address, 255.255.255.255. * * @param {string} [address] - An optional target IPv4 or IPv6 address */ broadcast(address = null) { try { if (!this._networkAvailable) return; // Try to parse strings as : if (typeof address === 'string') { const [host, portstr] = address.split(':'); const port = parseInt(portstr) || this.port; address = Gio.InetSocketAddress.new_from_string(host, port); } // If we succeed, remember this host if (address instanceof Gio.InetSocketAddress) { this._allowed.add(address.address.to_string()); // Broadcast to the network if no address is specified } else { debug('Broadcasting to LAN'); address = this._udp_address; } // Broadcast on each open socket if (this._udp6 !== null) this._udp6.send_to(address, this.identity.serialize(), null); if (this._udp4 !== null) this._udp4.send_to(address, this.identity.serialize(), null); } catch (e) { debug(e, address); } } buildIdentity() { // Chain-up, then add the TCP port super.buildIdentity(); this.identity.body.tcpPort = this.port; } start() { if (this.active) return; // Ensure a certificate exists if (this.certificate === null) this._initCertificate(); // Start TCP/UDP listeners try { if (this._tcp === null) this._initTcpListener(); if (this._udp4 === null && this._udp6 === null) this._initUdpListener(); } catch (e) { // Known case of another application using the protocol defined port if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.ADDRESS_IN_USE)) { e.name = _('Port already in use'); e.url = `${Config.PACKAGE_URL}/wiki/Error#port-already-in-use`; } throw e; } // Monitor network changes if (this._networkChangedId === 0) { this._networkAvailable = this._networkMonitor.network_available; this._networkChangedId = this._networkMonitor.connect( 'network-changed', this._onNetworkChanged.bind(this) ); } this._active = true; this.notify('active'); } stop() { if (this._networkChangedId) { this._networkMonitor.disconnect(this._networkChangedId); this._networkChangedId = 0; this._networkAvailable = false; } if (this._tcp !== null) { this._tcp.stop(); this._tcp.close(); this._tcp = null; } if (this._udp6 !== null) { this._udp6_source.destroy(); this._udp6_stream.close(null); this._udp6.close(); this._udp6 = null; } if (this._udp4 !== null) { this._udp4_source.destroy(); this._udp4_stream.close(null); this._udp4.close(); this._udp4 = null; } for (const channel of this.channels.values()) channel.close(); this._active = false; this.notify('active'); } destroy() { try { this.stop(); } catch (e) { debug(e); } } }); /** * Lan Channel * * This class essentially just extends Core.Channel to set TCP socket options * and negotiate TLS encrypted connections. */ var Channel = GObject.registerClass({ GTypeName: 'GSConnectLanChannel', }, class LanChannel extends Core.Channel { _init(params) { super._init(); Object.assign(this, params); } get address() { return `lan://${this.host}:${this.port}`; } get certificate() { if (this._certificate === undefined) this._certificate = null; return this._certificate; } set certificate(certificate) { this._certificate = certificate; } get peer_certificate() { if (this._connection instanceof Gio.TlsConnection) return this._connection.get_peer_certificate(); return null; } get host() { if (this._host === undefined) this._host = null; return this._host; } set host(host) { this._host = host; } get port() { if (this._port === undefined) { if (this.identity && this.identity.body.tcpPort) this._port = this.identity.body.tcpPort; else return DEFAULT_PORT; } return this._port; } set port(port) { this._port = port; } /** * Handshake Gio.TlsConnection * * @param {Gio.TlsConnection} connection - A TLS connection * @return {Promise} A promise for the operation */ _handshake(connection) { return new Promise((resolve, reject) => { connection.validation_flags = Gio.TlsCertificateFlags.EXPIRED; connection.authentication_mode = Gio.TlsAuthenticationMode.REQUIRED; connection.handshake_async( GLib.PRIORITY_DEFAULT, this.cancellable, (connection, res) => { try { resolve(connection.handshake_finish(res)); } catch (e) { reject(e); } } ); }); } /** * Authenticate a TLS connection. * * @param {Gio.TlsConnection} connection - A TLS connection * @return {Promise} A promise for the operation */ async _authenticate(connection) { // Standard TLS Handshake await this._handshake(connection); // Get a settings object for the device let settings; if (this.device) { settings = this.device.settings; } else { const id = this.identity.body.deviceId; settings = new Gio.Settings({ settings_schema: Config.GSCHEMA.lookup( 'org.gnome.Shell.Extensions.GSConnect.Device', true ), path: `/org/gnome/shell/extensions/gsconnect/device/${id}/`, }); } // If we have a certificate for this deviceId, we can verify it const cert_pem = settings.get_string('certificate-pem'); if (cert_pem !== '') { let certificate = null; let verified = false; try { certificate = Gio.TlsCertificate.new_from_pem(cert_pem, -1); verified = certificate.is_same(connection.peer_certificate); } catch (e) { logError(e); } /* The certificate is incorrect for one of two reasons, but both * result in us resetting the certificate and unpairing the device. * * If the certificate failed to load, it is probably corrupted or * otherwise invalid. In this case, if we try to continue we will * certainly crash the Android app. * * If the certificate did not match what we expected the obvious * thing to do is to notify the user, however experience tells us * this is a result of the user doing something masochistic like * nuking the Android app data or copying settings between machines. */ if (verified === false) { if (this.device) { this.device.unpair(); } else { settings.reset('paired'); settings.reset('certificate-pem'); } const name = this.identity.body.deviceName; throw new Error(`${name}: Authentication Failure`); } } return connection; } /** * Wrap the connection in Gio.TlsClientConnection and initiate handshake * * @param {Gio.TcpConnection} connection - The unauthenticated connection * @return {Gio.TlsClientConnection} The authenticated connection */ _encryptClient(connection) { _configureSocket(connection); connection = Gio.TlsClientConnection.new( connection, connection.socket.remote_address ); connection.set_certificate(this.certificate); return this._authenticate(connection); } /** * Wrap the connection in Gio.TlsServerConnection and initiate handshake * * @param {Gio.TcpConnection} connection - The unauthenticated connection * @return {Gio.TlsServerConnection} The authenticated connection */ _encryptServer(connection) { _configureSocket(connection); connection = Gio.TlsServerConnection.new(connection, this.certificate); // We're the server so we trust-on-first-use and verify after const _id = connection.connect('accept-certificate', (connection) => { connection.disconnect(_id); return true; }); return this._authenticate(connection); } /** * Read the identity packet from the new connection * * @param {Gio.SocketConnection} connection - An unencrypted socket * @return {Promise} A promise for the operation */ _receiveIdent(connection) { return new Promise((resolve, reject) => { // In principle this disposable wrapper could buffer more than the // identity packet, but in practice the remote device shouldn't send // any more data until the TLS connection is negotiated. const stream = new Gio.DataInputStream({ base_stream: connection.input_stream, close_base_stream: false, }); stream.read_line_async( GLib.PRIORITY_DEFAULT, this.cancellable, (stream, res) => { try { const data = stream.read_line_finish_utf8(res)[0]; stream.close(null); // Store the identity as an object property this.identity = new Core.Packet(data); // Reject connections without a deviceId if (!this.identity.body.deviceId) throw new Error('missing deviceId'); resolve(); } catch (e) { reject(e); } } ); }); } /** * Write our identity packet to the new connection * * @param {Gio.SocketConnection} connection - An unencrypted socket * @return {Promise} A promise for the operation */ _sendIdent(connection) { return new Promise((resolve, reject) => { connection.get_output_stream().write_all_async( this.backend.identity.serialize(), GLib.PRIORITY_DEFAULT, this.cancellable, (stream, res) => { try { resolve(stream.write_all_finish(res)); } catch (e) { reject(e); } } ); }); } /** * Negotiate an incoming connection * * @param {Gio.TcpConnection} connection - The incoming connection */ async accept(connection) { debug(`${this.address} (${this.uuid})`); try { this._connection = connection; this.backend.channels.set(this.address, this); await this._receiveIdent(this._connection); this._connection = await this._encryptClient(connection); } catch (e) { this.close(); throw e; } } /** * Negotiate an outgoing connection * * @param {Gio.SocketConnection} connection - The remote connection */ async open(connection) { debug(`${this.address} (${this.uuid})`); try { this._connection = connection; this.backend.channels.set(this.address, this); await this._sendIdent(this._connection); this._connection = await this._encryptServer(connection); } catch (e) { this.close(); throw e; } } /** * Close all streams associated with this channel, silencing any errors */ close() { if (this.closed) return; debug(`${this.address} (${this.uuid})`); this._closed = true; this.notify('closed'); this.backend.channels.delete(this.address); this.cancellable.cancel(); if (this._connection) this._connection.close_async(GLib.PRIORITY_DEFAULT, null, null); if (this.input_stream) this.input_stream.close_async(GLib.PRIORITY_DEFAULT, null, null); if (this.output_stream) this.output_stream.close_async(GLib.PRIORITY_DEFAULT, null, null); } async download(packet, target, cancellable = null) { const openConnection = new Promise((resolve, reject) => { const client = new Gio.SocketClient({enable_proxy: false}); const address = Gio.InetSocketAddress.new_from_string( this.host, packet.payloadTransferInfo.port ); client.connect_async(address, cancellable, (client, res) => { try { resolve(client.connect_finish(res)); } catch (e) { reject(e); } }); }); let connection = await openConnection; connection = await this._encryptClient(connection); const source = connection.get_input_stream(); // Start the transfer const transferredSize = await this._transfer(source, target, cancellable); // If we get less than expected, we've certainly got corruption if (transferredSize < packet.payloadSize) { throw new Gio.IOErrorEnum({ code: Gio.IOErrorEnum.FAILED, message: `Incomplete: ${transferredSize}/${packet.payloadSize}`, }); // TODO: sometimes kdeconnect-android under-reports a file's size // https://github.com/GSConnect/gnome-shell-extension-gsconnect/issues/1157 } else if (transferredSize > packet.payloadSize) { logError(new Gio.IOErrorEnum({ code: Gio.IOErrorEnum.FAILED, message: `Extra Data: ${transferredSize - packet.payloadSize}`, })); } } async upload(packet, source, size, cancellable = null) { // Start listening on the first available port between 1739-1764 const listener = new Gio.SocketListener(); let port = TRANSFER_MIN; while (port <= TRANSFER_MAX) { try { listener.add_inet_port(port, null); break; } catch (e) { if (port < TRANSFER_MAX) { port++; continue; } else { throw e; } } } // Listen for the incoming connection const acceptConnection = new Promise((resolve, reject) => { listener.accept_async( cancellable, (listener, res, source_object) => { try { resolve(listener.accept_finish(res)[0]); } catch (e) { reject(e); } } ); }); // Notify the device we're ready packet.body.payloadHash = this.checksum; packet.payloadSize = size; packet.payloadTransferInfo = {port: port}; this.sendPacket(new Core.Packet(packet)); // Accept the connection and configure the channel let connection = await acceptConnection; connection = await this._encryptServer(connection); const target = connection.get_output_stream(); // Start the transfer const transferredSize = await this._transfer(source, target, cancellable); if (transferredSize !== size) { throw new Gio.IOErrorEnum({ code: Gio.IOErrorEnum.PARTIAL_INPUT, message: 'Transfer incomplete', }); } } _transfer(source, target, cancellable) { return new Promise((resolve, reject) => { target.splice_async( source, (Gio.OutputStreamSpliceFlags.CLOSE_SOURCE | Gio.OutputStreamSpliceFlags.CLOSE_TARGET), GLib.PRIORITY_DEFAULT, cancellable, (target, res) => { try { resolve(target.splice_finish(res)); } catch (e) { reject(e); } } ); }); } async rejectTransfer(packet) { try { if (!packet || !packet.hasPayload()) return; if (packet.payloadTransferInfo.port === undefined) return; let connection = await new Promise((resolve, reject) => { const client = new Gio.SocketClient({enable_proxy: false}); const address = Gio.InetSocketAddress.new_from_string( this.host, packet.payloadTransferInfo.port ); client.connect_async(address, null, (client, res) => { try { resolve(client.connect_finish(res)); } catch (e) { resolve(); } }); }); connection = await this._encryptClient(connection); connection.close_async(GLib.PRIORITY_DEFAULT, null, null); } catch (e) { debug(e, this.device.name); } } }); gnome-shell-extension-gsconnect-50/src/service/components/000077500000000000000000000000001421543444100241235ustar00rootroot00000000000000gnome-shell-extension-gsconnect-50/src/service/components/__init__.js000066400000000000000000000024401421543444100262200ustar00rootroot00000000000000'use strict'; /* * Singleton Tracker */ const Default = new Map(); /** * Acquire a reference to a component. Calls to this function should always be * followed by a call to `release()`. * * @param {string} name - The module name * @return {*} The default instance of a component */ function acquire(name) { let component; try { let info = Default.get(name); if (info === undefined) { const module = imports.service.components[name]; info = { instance: new module.Component(), refcount: 0, }; Default.set(name, info); } info.refcount++; component = info.instance; } catch (e) { debug(e, name); } return component; } /** * Release a reference on a component. If the caller was the last reference * holder, the component will be freed. * * @param {string} name - The module name * @return {null} A %null value, useful for overriding a traced variable */ function release(name) { try { const info = Default.get(name); if (info.refcount === 1) { info.instance.destroy(); Default.delete(name); } info.refcount--; } catch (e) { debug(e, name); } return null; } gnome-shell-extension-gsconnect-50/src/service/components/atspi.js000066400000000000000000000220541421543444100256040ustar00rootroot00000000000000'use strict'; imports.gi.versions.Atspi = '2.0'; const Atspi = imports.gi.Atspi; const Gdk = imports.gi.Gdk; /** * Printable ASCII range */ const _ASCII = /[\x20-\x7E]/; /** * Modifier Keycode Defaults */ const XKeycode = { Alt_L: 0x40, Control_L: 0x25, Shift_L: 0x32, Super_L: 0x85, }; /** * A thin wrapper around Atspi for X11 sessions without Pipewire support. */ var Controller = class { constructor() { // Atspi.init() return 2 on fail, but still marks itself as inited. We // uninit before throwing an error otherwise any future call to init() // will appear successful and other calls will cause GSConnect to exit. // See: https://gitlab.gnome.org/GNOME/at-spi2-core/blob/master/atspi/atspi-misc.c if (Atspi.init() === 2) { this.destroy(); throw new Error('Failed to start AT-SPI'); } try { this._display = Gdk.Display.get_default(); this._seat = this._display.get_default_seat(); this._pointer = this._seat.get_pointer(); } catch (e) { this.destroy(); throw e; } // Try to read modifier keycodes from Gdk try { const keymap = Gdk.Keymap.get_for_display(this._display); let modifier; modifier = keymap.get_entries_for_keyval(Gdk.KEY_Alt_L)[1][0]; XKeycode.Alt_L = modifier.keycode; modifier = keymap.get_entries_for_keyval(Gdk.KEY_Control_L)[1][0]; XKeycode.Control_L = modifier.keycode; modifier = keymap.get_entries_for_keyval(Gdk.KEY_Shift_L)[1][0]; XKeycode.Shift_L = modifier.keycode; modifier = keymap.get_entries_for_keyval(Gdk.KEY_Super_L)[1][0]; XKeycode.Super_L = modifier.keycode; } catch (e) { debug('using default modifier keycodes'); } } /* * Pointer events */ clickPointer(button) { try { const [, x, y] = this._pointer.get_position(); const monitor = this._display.get_monitor_at_point(x, y); const scale = monitor.get_scale_factor(); Atspi.generate_mouse_event(scale * x, scale * y, `b${button}c`); } catch (e) { logError(e); } } doubleclickPointer(button) { try { const [, x, y] = this._pointer.get_position(); const monitor = this._display.get_monitor_at_point(x, y); const scale = monitor.get_scale_factor(); Atspi.generate_mouse_event(scale * x, scale * y, `b${button}d`); } catch (e) { logError(e); } } movePointer(dx, dy) { try { const [, x, y] = this._pointer.get_position(); const monitor = this._display.get_monitor_at_point(x, y); const scale = monitor.get_scale_factor(); Atspi.generate_mouse_event(scale * dx, scale * dy, 'rel'); } catch (e) { logError(e); } } pressPointer(button) { try { const [, x, y] = this._pointer.get_position(); const monitor = this._display.get_monitor_at_point(x, y); const scale = monitor.get_scale_factor(); Atspi.generate_mouse_event(scale * x, scale * y, `b${button}p`); } catch (e) { logError(e); } } releasePointer(button) { try { const [, x, y] = this._pointer.get_position(); const monitor = this._display.get_monitor_at_point(x, y); const scale = monitor.get_scale_factor(); Atspi.generate_mouse_event(scale * x, scale * y, `b${button}r`); } catch (e) { logError(e); } } scrollPointer(dx, dy) { if (dy > 0) this.clickPointer(4); else if (dy < 0) this.clickPointer(5); } /* * Phony virtual keyboard helpers */ _modeLock(keycode) { Atspi.generate_keyboard_event( keycode, null, Atspi.KeySynthType.PRESS ); } _modeUnlock(keycode) { Atspi.generate_keyboard_event( keycode, null, Atspi.KeySynthType.RELEASE ); } /* * Simulate a printable-ASCII character. * */ _pressASCII(key, modifiers) { try { // Press Modifiers if (modifiers & Gdk.ModifierType.MOD1_MASK) this._modeLock(XKeycode.Alt_L); if (modifiers & Gdk.ModifierType.CONTROL_MASK) this._modeLock(XKeycode.Control_L); if (modifiers & Gdk.ModifierType.SHIFT_MASK) this._modeLock(XKeycode.Shift_L); if (modifiers & Gdk.ModifierType.SUPER_MASK) this._modeLock(XKeycode.Super_L); Atspi.generate_keyboard_event( 0, key, Atspi.KeySynthType.STRING ); // Release Modifiers if (modifiers & Gdk.ModifierType.MOD1_MASK) this._modeUnlock(XKeycode.Alt_L); if (modifiers & Gdk.ModifierType.CONTROL_MASK) this._modeUnlock(XKeycode.Control_L); if (modifiers & Gdk.ModifierType.SHIFT_MASK) this._modeUnlock(XKeycode.Shift_L); if (modifiers & Gdk.ModifierType.SUPER_MASK) this._modeUnlock(XKeycode.Super_L); } catch (e) { logError(e); } } _pressKeysym(keysym, modifiers) { try { // Press Modifiers if (modifiers & Gdk.ModifierType.MOD1_MASK) this._modeLock(XKeycode.Alt_L); if (modifiers & Gdk.ModifierType.CONTROL_MASK) this._modeLock(XKeycode.Control_L); if (modifiers & Gdk.ModifierType.SHIFT_MASK) this._modeLock(XKeycode.Shift_L); if (modifiers & Gdk.ModifierType.SUPER_MASK) this._modeLock(XKeycode.Super_L); Atspi.generate_keyboard_event( keysym, null, Atspi.KeySynthType.PRESSRELEASE | Atspi.KeySynthType.SYM ); // Release Modifiers if (modifiers & Gdk.ModifierType.MOD1_MASK) this._modeUnlock(XKeycode.Alt_L); if (modifiers & Gdk.ModifierType.CONTROL_MASK) this._modeUnlock(XKeycode.Control_L); if (modifiers & Gdk.ModifierType.SHIFT_MASK) this._modeUnlock(XKeycode.Shift_L); if (modifiers & Gdk.ModifierType.SUPER_MASK) this._modeUnlock(XKeycode.Super_L); } catch (e) { logError(e); } } /** * Simulate the composition of a unicode character with: * Control+Shift+u, [hex], Return * * @param {number} key - An XKeycode * @param {number} modifiers - A modifier mask */ _pressUnicode(key, modifiers) { try { if (modifiers > 0) log('GSConnect: ignoring modifiers for unicode keyboard event'); // TODO: Using Control and Shift keysym is not working (it triggers // key release). Probably using LOCKMODIFIERS will not work either // as unlocking the modifier will not trigger a release // Activate compose sequence this._modeLock(XKeycode.Control_L); this._modeLock(XKeycode.Shift_L); this.pressreleaseKeysym(Gdk.KEY_U); this._modeUnlock(XKeycode.Control_L); this._modeUnlock(XKeycode.Shift_L); // Enter the unicode sequence const ucode = key.charCodeAt(0).toString(16); let keysym; for (let h = 0, len = ucode.length; h < len; h++) { keysym = Gdk.unicode_to_keyval(ucode.charAt(h).codePointAt(0)); this.pressreleaseKeysym(keysym); } // Finish the compose sequence this.pressreleaseKeysym(Gdk.KEY_Return); } catch (e) { logError(e); } } /* * Keyboard Events */ pressKeysym(keysym) { Atspi.generate_keyboard_event( keysym, null, Atspi.KeySynthType.PRESS | Atspi.KeySynthType.SYM ); } releaseKeysym(keysym) { Atspi.generate_keyboard_event( keysym, null, Atspi.KeySynthType.RELEASE | Atspi.KeySynthType.SYM ); } pressreleaseKeysym(keysym) { Atspi.generate_keyboard_event( keysym, null, Atspi.KeySynthType.PRESSRELEASE | Atspi.KeySynthType.SYM ); } pressKey(input, modifiers) { // We were passed a keysym if (typeof input === 'number') this._pressKeysym(input, modifiers); // Regular ASCII else if (_ASCII.test(input)) this._pressASCII(input, modifiers); // Unicode else this._pressUnicode(input, modifiers); } destroy() { try { Atspi.exit(); } catch (e) { // Silence errors } } }; gnome-shell-extension-gsconnect-50/src/service/components/clipboard.js000066400000000000000000000170661421543444100264320ustar00rootroot00000000000000'use strict'; const Gdk = imports.gi.Gdk; const GLib = imports.gi.GLib; const Gtk = imports.gi.Gtk; const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; const DBUS_NAME = 'org.gnome.Shell.Extensions.GSConnect.Clipboard'; const DBUS_PATH = '/org/gnome/Shell/Extensions/GSConnect/Clipboard'; var Clipboard = GObject.registerClass({ GTypeName: 'GSConnectClipboard', Properties: { 'text': GObject.ParamSpec.string( 'text', 'Text Content', 'The current text content of the clipboard', GObject.ParamFlags.READWRITE, '' ), }, }, class Clipboard extends GObject.Object { _init() { super._init(); this._cancellable = new Gio.Cancellable(); this._clipboard = null; this._ownerChangeId = 0; this._nameWatcherId = Gio.bus_watch_name( Gio.BusType.SESSION, DBUS_NAME, Gio.BusNameWatcherFlags.NONE, this._onNameAppeared.bind(this), this._onNameVanished.bind(this) ); } get text() { if (this._text === undefined) this._text = ''; return this._text; } set text(content) { if (this.text === content) return; this._text = content; this.notify('text'); if (typeof content !== 'string') return; if (this._clipboard instanceof Gtk.Clipboard) this._clipboard.set_text(content, -1); if (this._clipboard instanceof Gio.DBusProxy) this._proxySetText(content); } async _onNameAppeared(connection, name, name_owner) { try { // Cleanup the GtkClipboard if (this._clipboard && this._ownerChangeId > 0) { this._clipboard.disconnect(this._ownerChangeId); this._ownerChangeId = 0; } // Create a proxy for the remote clipboard this._clipboard = new Gio.DBusProxy({ g_bus_type: Gio.BusType.SESSION, g_name: DBUS_NAME, g_object_path: DBUS_PATH, g_interface_name: DBUS_NAME, g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES, }); await new Promise((resolve, reject) => { this._clipboard.init_async( GLib.PRIORITY_DEFAULT, this._cancellable, (proxy, res) => { try { resolve(proxy.init_finish(res)); } catch (e) { reject(e); } } ); }); this._ownerChangeId = this._clipboard.connect( 'g-signal', this._onOwnerChange.bind(this) ); this._onOwnerChange(); } catch (e) { if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) { debug(e); this._onNameVanished(null, null); } } } _onNameVanished(connection, name) { if (this._clipboard && this._ownerChangeId > 0) { this._clipboard.disconnect(this._ownerChangeId); this._clipboardChangedId = 0; } const display = Gdk.Display.get_default(); this._clipboard = Gtk.Clipboard.get_default(display); this._ownerChangeId = this._clipboard.connect( 'owner-change', this._onOwnerChange.bind(this) ); this._onOwnerChange(); } async _onOwnerChange() { try { if (this._clipboard instanceof Gtk.Clipboard) await this._gtkUpdateText(); else if (this._clipboard instanceof Gio.DBusProxy) await this._proxyUpdateText(); } catch (e) { if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) debug(e); } } _applyUpdate(text) { if (typeof text !== 'string' || this.text === text) return; this._text = text; this.notify('text'); } /* * Proxy Clipboard */ _proxyGetMimetypes() { return new Promise((resolve, reject) => { this._clipboard.call( 'GetMimetypes', null, Gio.DBusCallFlags.NO_AUTO_START, -1, this._cancellable, (proxy, res) => { try { const reply = proxy.call_finish(res); resolve(reply.deepUnpack()[0]); } catch (e) { Gio.DBusError.strip_remote_error(e); reject(e); } } ); }); } _proxyGetText() { return new Promise((resolve, reject) => { this._clipboard.call( 'GetText', null, Gio.DBusCallFlags.NO_AUTO_START, -1, this._cancellable, (proxy, res) => { try { const reply = proxy.call_finish(res); resolve(reply.deepUnpack()[0]); } catch (e) { Gio.DBusError.strip_remote_error(e); reject(e); } } ); }); } _proxySetText(text) { this._clipboard.call( 'SetText', new GLib.Variant('(s)', [text]), Gio.DBusCallFlags.NO_AUTO_START, -1, this._cancellable, (proxy, res) => { try { proxy.call_finish(res); } catch (e) { Gio.DBusError.strip_remote_error(e); debug(e); } } ); } async _proxyUpdateText() { const mimetypes = await this._proxyGetMimetypes(); // Special case for a cleared clipboard if (mimetypes.length === 0) return this._applyUpdate(''); // Special case to ignore copied files if (mimetypes.includes('text/uri-list')) return; const text = await this._proxyGetText(); this._applyUpdate(text); } /* * GtkClipboard */ _gtkGetMimetypes() { return new Promise((resolve, reject) => { this._clipboard.request_targets((clipboard, atoms) => resolve(atoms)); }); } _gtkGetText() { return new Promise((resolve, reject) => { this._clipboard.request_text((clipboard, text) => resolve(text)); }); } async _gtkUpdateText() { const mimetypes = await this._gtkGetMimetypes(); // Special case for a cleared clipboard if (mimetypes.length === 0) return this._applyUpdate(''); // Special case to ignore copied files if (mimetypes.includes('text/uri-list')) return; const text = await this._gtkGetText(); this._applyUpdate(text); } destroy() { if (this._cancellable.is_cancelled()) return; this._cancellable.cancel(); if (this._clipboard && this._ownerChangeId > 0) { this._clipboard.disconnect(this._ownerChangeId); this._ownerChangedId = 0; } if (this._nameWatcherId > 0) { Gio.bus_unwatch_name(this._nameWatcherId); this._nameWatcherId = 0; } } }); /** * The service class for this component */ var Component = Clipboard; gnome-shell-extension-gsconnect-50/src/service/components/contacts.js000066400000000000000000000501711421543444100263030ustar00rootroot00000000000000'use strict'; const ByteArray = imports.byteArray; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Config = imports.config; var HAVE_EDS = true; var EBook = null; var EBookContacts = null; var EDataServer = null; try { EBook = imports.gi.EBook; EBookContacts = imports.gi.EBookContacts; EDataServer = imports.gi.EDataServer; } catch (e) { HAVE_EDS = false; } /** * A store for contacts */ var Store = GObject.registerClass({ GTypeName: 'GSConnectContactsStore', Properties: { 'context': GObject.ParamSpec.string( 'context', 'Context', 'Used as the cache directory, relative to Config.CACHEDIR', GObject.ParamFlags.CONSTRUCT_ONLY | GObject.ParamFlags.READWRITE, null ), }, Signals: { 'contact-added': { flags: GObject.SignalFlags.RUN_FIRST, param_types: [GObject.TYPE_STRING], }, 'contact-removed': { flags: GObject.SignalFlags.RUN_FIRST, param_types: [GObject.TYPE_STRING], }, 'contact-changed': { flags: GObject.SignalFlags.RUN_FIRST, param_types: [GObject.TYPE_STRING], }, }, }, class Store extends GObject.Object { _init(context = null) { super._init({ context: context, }); this._cacheData = {}; this._edsPrepared = false; } /** * Parse an EContact and add it to the store. * * @param {EBookContacts.Contact} econtact - an EContact to parse * @param {string} [origin] - an optional origin string */ async _parseEContact(econtact, origin = 'desktop') { try { const contact = { id: econtact.id, name: _('Unknown Contact'), numbers: [], origin: origin, timestamp: 0, }; // Try to get a contact name if (econtact.full_name) contact.name = econtact.full_name; // Parse phone numbers const nums = econtact.get_attributes(EBookContacts.ContactField.TEL); for (const attr of nums) { const number = { value: attr.get_value(), type: 'unknown', }; if (attr.has_type('CELL')) number.type = 'cell'; else if (attr.has_type('HOME')) number.type = 'home'; else if (attr.has_type('WORK')) number.type = 'work'; contact.numbers.push(number); } // Try and get a contact photo const photo = econtact.photo; if (photo) { if (photo.type === EBookContacts.ContactPhotoType.INLINED) { const data = photo.get_inlined()[0]; contact.avatar = await this.storeAvatar(data); } else if (photo.type === EBookContacts.ContactPhotoType.URI) { const uri = econtact.photo.get_uri(); contact.avatar = uri.replace('file://', ''); } } this.add(contact, false); } catch (e) { logError(e, `Failed to parse VCard contact ${econtact.id}`); } } /* * EDS Helpers */ _getEBookClient(source, cancellable = null) { return new Promise((resolve, reject) => { EBook.BookClient.connect(source, 0, cancellable, (source, res) => { try { resolve(EBook.BookClient.connect_finish(res)); } catch (e) { reject(e); } }); }); } _getEBookView(client, query = '', cancellable = null) { return new Promise((resolve, reject) => { client.get_view(query, cancellable, (client, res) => { try { resolve(client.get_view_finish(res)[1]); } catch (e) { reject(e); } }); }); } _getEContacts(client, query = '', cancellable = null) { return new Promise((resolve, reject) => { client.get_contacts(query, cancellable, (client, res) => { try { resolve(client.get_contacts_finish(res)[1]); } catch (e) { debug(e); resolve([]); } }); }); } _getESourceRegistry(cancellable = null) { return new Promise((resolve, reject) => { EDataServer.SourceRegistry.new(cancellable, (registry, res) => { try { resolve(EDataServer.SourceRegistry.new_finish(res)); } catch (e) { reject(e); } }); }); } /* * AddressBook DBus callbacks */ _onObjectsAdded(connection, sender, path, iface, signal, params) { try { const adds = params.get_child_value(0).get_strv(); // NOTE: sequential pairs of vcard, id for (let i = 0, len = adds.length; i < len; i += 2) { try { const vcard = adds[i]; const econtact = EBookContacts.Contact.new_from_vcard(vcard); this._parseEContact(econtact); } catch (e) { debug(e); } } } catch (e) { debug(e); } } _onObjectsRemoved(connection, sender, path, iface, signal, params) { try { const changes = params.get_child_value(0).get_strv(); for (const id of changes) { try { this.remove(id, false); } catch (e) { debug(e); } } } catch (e) { debug(e); } } _onObjectsModified(connection, sender, path, iface, signal, params) { try { const changes = params.get_child_value(0).get_strv(); // NOTE: sequential pairs of vcard, id for (let i = 0, len = changes.length; i < len; i += 2) { try { const vcard = changes[i]; const econtact = EBookContacts.Contact.new_from_vcard(vcard); this._parseEContact(econtact); } catch (e) { debug(e); } } } catch (e) { debug(e); } } /* * SourceRegistryWatcher callbacks */ async _onAppeared(watcher, source) { try { // Get an EBookClient and EBookView const uid = source.get_uid(); const client = await this._getEBookClient(source); const view = await this._getEBookView(client, 'exists "tel"'); // Watch the view for changes to the address book const connection = view.get_connection(); const objectPath = view.get_object_path(); view._objectsAddedId = connection.signal_subscribe( null, 'org.gnome.evolution.dataserver.AddressBookView', 'ObjectsAdded', objectPath, null, Gio.DBusSignalFlags.NONE, this._onObjectsAdded.bind(this) ); view._objectsRemovedId = connection.signal_subscribe( null, 'org.gnome.evolution.dataserver.AddressBookView', 'ObjectsRemoved', objectPath, null, Gio.DBusSignalFlags.NONE, this._onObjectsRemoved.bind(this) ); view._objectsModifiedId = connection.signal_subscribe( null, 'org.gnome.evolution.dataserver.AddressBookView', 'ObjectsModified', objectPath, null, Gio.DBusSignalFlags.NONE, this._onObjectsModified.bind(this) ); view.start(); // Store the EBook in a map this._ebooks.set(uid, { source: source, client: client, view: view, }); } catch (e) { debug(e); } } _onDisappeared(watcher, source) { try { const uid = source.get_uid(); const ebook = this._ebooks.get(uid); if (ebook === undefined) return; // Disconnect the EBookView if (ebook.view) { const connection = ebook.view.get_connection(); connection.signal_unsubscribe(ebook.view._objectsAddedId); connection.signal_unsubscribe(ebook.view._objectsRemovedId); connection.signal_unsubscribe(ebook.view._objectsModifiedId); ebook.view.stop(); } this._ebooks.delete(uid); } catch (e) { debug(e); } } async _initEvolutionDataServer() { try { if (this._edsPrepared) return; this._edsPrepared = true; this._ebooks = new Map(); // Get the current EBooks const registry = await this._getESourceRegistry(); for (const source of registry.list_sources('Address Book')) await this._onAppeared(null, source); // Watch for new and removed sources this._watcher = new EDataServer.SourceRegistryWatcher({ registry: registry, extension_name: 'Address Book', }); this._appearedId = this._watcher.connect( 'appeared', this._onAppeared.bind(this) ); this._disappearedId = this._watcher.connect( 'disappeared', this._onDisappeared.bind(this) ); } catch (e) { const service = Gio.Application.get_default(); if (service !== null) service.notify_error(e); else logError(e); } } *[Symbol.iterator]() { const contacts = Object.values(this._cacheData); for (let i = 0, len = contacts.length; i < len; i++) yield contacts[i]; } get contacts() { return Object.values(this._cacheData); } get context() { if (this._context === undefined) this._context = null; return this._context; } set context(context) { this._context = context; this._cacheDir = Gio.File.new_for_path(Config.CACHEDIR); if (context !== null) this._cacheDir = this._cacheDir.get_child(context); GLib.mkdir_with_parents(this._cacheDir.get_path(), 448); this._cacheFile = this._cacheDir.get_child('contacts.json'); } /** * Save a ByteArray to file and return the path * * @param {ByteArray} contents - An image ByteArray * @return {string|undefined} File path or %undefined on failure */ storeAvatar(contents) { return new Promise((resolve, reject) => { const md5 = GLib.compute_checksum_for_data( GLib.ChecksumType.MD5, contents ); const file = this._cacheDir.get_child(`${md5}`); if (file.query_exists(null)) { resolve(file.get_path()); } else { file.replace_contents_bytes_async( new GLib.Bytes(contents), null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, null, (file, res) => { try { file.replace_contents_finish(res); resolve(file.get_path()); } catch (e) { debug(e, 'Storing avatar'); resolve(undefined); } } ); } }); } /** * Query the Store for a contact by name and/or number. * * @param {Object} query - A query object * @param {string} [query.name] - The contact's name * @param {string} query.number - The contact's number * @return {Object} A contact object */ query(query) { // First look for an existing contact by number const contacts = this.contacts; const matches = []; const qnumber = query.number.toPhoneNumber(); for (let i = 0, len = contacts.length; i < len; i++) { const contact = contacts[i]; for (const num of contact.numbers) { const cnumber = num.value.toPhoneNumber(); if (qnumber.endsWith(cnumber) || cnumber.endsWith(qnumber)) { // If no query name or exact match, return immediately if (!query.name || query.name === contact.name) return contact; // Otherwise we might find an exact name match that shares // the number with another contact matches.push(contact); } } } // Return the first match (pretty much what Android does) if (matches.length > 0) return matches[0]; // No match; return a mock contact with a unique ID let id = GLib.uuid_string_random(); while (this._cacheData.hasOwnProperty(id)) id = GLib.uuid_string_random(); return { id: id, name: query.name || query.number, numbers: [{value: query.number, type: 'unknown'}], origin: 'gsconnect', }; } get_contact(position) { if (this._cacheData[position] !== undefined) return this._cacheData[position]; return null; } /** * Add a contact, checking for validity * * @param {Object} contact - A contact object * @param {boolean} write - Write to disk */ add(contact, write = true) { // Ensure the contact has a unique id if (!contact.id) { let id = GLib.uuid_string_random(); while (this._cacheData[id]) id = GLib.uuid_string_random(); contact.id = id; } // Ensure the contact has an origin if (!contact.origin) contact.origin = 'gsconnect'; // This is an updated contact if (this._cacheData[contact.id]) { this._cacheData[contact.id] = contact; this.emit('contact-changed', contact.id); // This is a new contact } else { this._cacheData[contact.id] = contact; this.emit('contact-added', contact.id); } // Write if requested if (write) this.save(); } /** * Remove a contact by id * * @param {string} id - The id of the contact to delete * @param {boolean} write - Write to disk */ remove(id, write = true) { // Only remove if the contact actually exists if (this._cacheData[id]) { delete this._cacheData[id]; this.emit('contact-removed', id); // Write if requested if (write) this.save(); } } /** * Lookup a contact for each address object in @addresses and return a * dictionary of address (eg. phone number) to contact object. * * { "555-5555": { "name": "...", "numbers": [], ... } } * * @param {Object[]} addresses - A list of address objects * @return {Object} A dictionary of phone numbers and contacts */ lookupAddresses(addresses) { const contacts = {}; // Lookup contacts for each address for (let i = 0, len = addresses.length; i < len; i++) { const address = addresses[i].address; contacts[address] = this.query({ number: address, }); } return contacts; } async clear() { try { const contacts = this.contacts; for (let i = 0, len = contacts.length; i < len; i++) await this.remove(contacts[i].id, false); await this.save(); } catch (e) { debug(e); } } /** * Update the contact store from a dictionary of our custom contact objects. * * @param {Object} json - an Object of contact Objects */ async update(json = {}) { try { let contacts = Object.values(json); for (let i = 0, len = contacts.length; i < len; i++) { const new_contact = contacts[i]; const contact = this._cacheData[new_contact.id]; if (!contact || new_contact.timestamp !== contact.timestamp) await this.add(new_contact, false); } // Prune contacts contacts = this.contacts; for (let i = 0, len = contacts.length; i < len; i++) { const contact = contacts[i]; if (!json[contact.id]) await this.remove(contact.id, false); } await this.save(); } catch (e) { debug(e, 'Updating contacts'); } } /** * Fetch and update the contact store from its source. * * The default function initializes the EDS server, or logs a debug message * if EDS is unavailable. Derived classes should request an update from the * remote source. */ async fetch() { try { if (this.context === null && HAVE_EDS) await this._initEvolutionDataServer(); else throw new Error('Evolution Data Server not available'); } catch (e) { debug(e); } } /** * Load the contacts from disk. */ async load() { try { this._cacheData = await new Promise((resolve, reject) => { this._cacheFile.load_contents_async(null, (file, res) => { try { const contents = file.load_contents_finish(res)[1]; resolve(JSON.parse(ByteArray.toString(contents))); } catch (e) { reject(e); } }); }); } catch (e) { debug(e); } finally { this.notify('context'); } } /** * Save the contacts to disk. */ async save() { // EDS is handling storage if (this.context === null && HAVE_EDS) return; if (this.__cache_lock) { this.__cache_queue = true; return; } try { this.__cache_lock = true; await new Promise((resolve, reject) => { this._cacheFile.replace_contents_bytes_async( new GLib.Bytes(JSON.stringify(this._cacheData, null, 2)), null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, null, (file, res) => { try { resolve(file.replace_contents_finish(res)); } catch (e) { reject(e); } } ); }); } catch (e) { debug(e); } finally { this.__cache_lock = false; if (this.__cache_queue) { this.__cache_queue = false; this.save(); } } } destroy() { if (this._watcher !== undefined) { this._watcher.disconnect(this._appearedId); this._watcher.disconnect(this._disappearedId); this._watcher = undefined; for (const ebook of this._ebooks.values()) this._onDisappeared(null, ebook.source); this._edsPrepared = false; } } }); /** * The service class for this component */ var Component = Store; gnome-shell-extension-gsconnect-50/src/service/components/input.js000066400000000000000000000422311421543444100256220ustar00rootroot00000000000000'use strict'; const Gdk = imports.gi.Gdk; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const SESSION_TIMEOUT = 15; const RemoteSession = GObject.registerClass({ GTypeName: 'GSConnectRemoteSession', Implements: [Gio.DBusInterface], Signals: { 'closed': { flags: GObject.SignalFlags.RUN_FIRST, }, }, }, class RemoteSession extends Gio.DBusProxy { _init(objectPath) { super._init({ g_bus_type: Gio.BusType.SESSION, g_name: 'org.gnome.Mutter.RemoteDesktop', g_object_path: objectPath, g_interface_name: 'org.gnome.Mutter.RemoteDesktop.Session', g_flags: Gio.DBusProxyFlags.NONE, }); this._started = false; } vfunc_g_signal(sender_name, signal_name, parameters) { if (signal_name === 'Closed') this.emit('closed'); } _call(name, parameters = null) { if (!this._started) return; this.call(name, parameters, Gio.DBusCallFlags.NONE, -1, null, null); } get session_id() { try { return this.get_cached_property('SessionId').unpack(); } catch (e) { return null; } } async start() { try { if (this._started) return; // Initialize the proxy await new Promise((resolve, reject) => { this.init_async( GLib.PRIORITY_DEFAULT, null, (proxy, res) => { try { proxy.init_finish(res); resolve(); } catch (e) { reject(e); } } ); }); // Start the session await new Promise((resolve, reject) => { this.call( 'Start', null, Gio.DBusCallFlags.NONE, -1, null, (proxy, res) => { try { resolve(proxy.call_finish(res)); } catch (e) { reject(e); } } ); }); this._started = true; } catch (e) { this.destroy(); Gio.DBusError.strip_remote_error(e); throw e; } } stop() { if (this._started) { this._started = false; this.call('Stop', null, Gio.DBusCallFlags.NONE, -1, null, null); } } _translateButton(button) { switch (button) { case Gdk.BUTTON_PRIMARY: return 0x110; case Gdk.BUTTON_MIDDLE: return 0x112; case Gdk.BUTTON_SECONDARY: return 0x111; case 4: return 0; // FIXME case 5: return 0x10F; // up } } movePointer(dx, dy) { this._call( 'NotifyPointerMotionRelative', GLib.Variant.new('(dd)', [dx, dy]) ); } pressPointer(button) { button = this._translateButton(button); this._call( 'NotifyPointerButton', GLib.Variant.new('(ib)', [button, true]) ); } releasePointer(button) { button = this._translateButton(button); this._call( 'NotifyPointerButton', GLib.Variant.new('(ib)', [button, false]) ); } clickPointer(button) { button = this._translateButton(button); this._call( 'NotifyPointerButton', GLib.Variant.new('(ib)', [button, true]) ); this._call( 'NotifyPointerButton', GLib.Variant.new('(ib)', [button, false]) ); } doubleclickPointer(button) { this.clickPointer(button); this.clickPointer(button); } scrollPointer(dx, dy) { // NOTE: NotifyPointerAxis only seems to work on Wayland, but maybe // NotifyPointerAxisDiscrete is the better choice anyways if (HAVE_WAYLAND) { this._call( 'NotifyPointerAxis', GLib.Variant.new('(ddu)', [dx, dy, 0]) ); this._call( 'NotifyPointerAxis', GLib.Variant.new('(ddu)', [0, 0, 1]) ); } else if (dy > 0) { this._call( 'NotifyPointerAxisDiscrete', GLib.Variant.new('(ui)', [Gdk.ScrollDirection.UP, 1]) ); } else if (dy < 0) { this._call( 'NotifyPointerAxisDiscrete', GLib.Variant.new('(ui)', [Gdk.ScrollDirection.UP, -1]) ); } } /* * Keyboard Events */ pressKeysym(keysym) { this._call( 'NotifyKeyboardKeysym', GLib.Variant.new('(ub)', [keysym, true]) ); } releaseKeysym(keysym) { this._call( 'NotifyKeyboardKeysym', GLib.Variant.new('(ub)', [keysym, false]) ); } pressreleaseKeysym(keysym) { this._call( 'NotifyKeyboardKeysym', GLib.Variant.new('(ub)', [keysym, true]) ); this._call( 'NotifyKeyboardKeysym', GLib.Variant.new('(ub)', [keysym, false]) ); } /* * High-level keyboard input */ pressKey(input, modifiers) { // Press Modifiers if (modifiers & Gdk.ModifierType.MOD1_MASK) this.pressKeysym(Gdk.KEY_Alt_L); if (modifiers & Gdk.ModifierType.CONTROL_MASK) this.pressKeysym(Gdk.KEY_Control_L); if (modifiers & Gdk.ModifierType.SHIFT_MASK) this.pressKeysym(Gdk.KEY_Shift_L); if (modifiers & Gdk.ModifierType.SUPER_MASK) this.pressKeysym(Gdk.KEY_Super_L); if (typeof input === 'string') { const keysym = Gdk.unicode_to_keyval(input.codePointAt(0)); this.pressreleaseKeysym(keysym); } else { this.pressreleaseKeysym(input); } // Release Modifiers if (modifiers & Gdk.ModifierType.MOD1_MASK) this.releaseKeysym(Gdk.KEY_Alt_L); if (modifiers & Gdk.ModifierType.CONTROL_MASK) this.releaseKeysym(Gdk.KEY_Control_L); if (modifiers & Gdk.ModifierType.SHIFT_MASK) this.releaseKeysym(Gdk.KEY_Shift_L); if (modifiers & Gdk.ModifierType.SUPER_MASK) this.releaseKeysym(Gdk.KEY_Super_L); } destroy() { if (this.__disposed === undefined) { this.__disposed = true; GObject.signal_handlers_destroy(this); } } }); class Controller { constructor() { this._nameAppearedId = 0; this._session = null; this._sessionCloseId = 0; this._sessionExpiry = 0; this._sessionExpiryId = 0; this._sessionStarting = false; // Watch for the RemoteDesktop portal this._nameWatcherId = Gio.bus_watch_name( Gio.BusType.SESSION, 'org.gnome.Mutter.RemoteDesktop', Gio.BusNameWatcherFlags.NONE, this._onNameAppeared.bind(this), this._onNameVanished.bind(this) ); } get connection() { if (this._connection === undefined) this._connection = null; return this._connection; } /** * Check if this is a Wayland session, specifically for distributions that * don't ship pipewire support (eg. Debian/Ubuntu). * * FIXME: this is a super ugly hack that should go away * * @return {boolean} %true if wayland is not supported */ _checkWayland() { if (HAVE_WAYLAND) { // eslint-disable-next-line no-global-assign HAVE_REMOTEINPUT = false; const service = Gio.Application.get_default(); if (service === null) return true; // First we're going to disabled the affected plugins on all devices for (const device of service.manager.devices.values()) { const supported = device.settings.get_strv('supported-plugins'); let index; if ((index = supported.indexOf('mousepad')) > -1) supported.splice(index, 1); if ((index = supported.indexOf('presenter')) > -1) supported.splice(index, 1); device.settings.set_strv('supported-plugins', supported); } // Second we need each backend to rebuild its identity packet and // broadcast the amended capabilities to the network for (const backend of service.manager.backends.values()) backend.buildIdentity(); service.manager.identify(); return true; } return false; } _onNameAppeared(connection, name, name_owner) { try { this._connection = connection; } catch (e) { logError(e); } } _onNameVanished(connection, name) { try { if (this._session !== null) this._onSessionClosed(this._session); } catch (e) { logError(e); } } _onSessionClosed(session) { // Disconnect from the session if (this._sessionClosedId > 0) { session.disconnect(this._sessionClosedId); this._sessionClosedId = 0; } // Destroy the session session.destroy(); this._session = null; } _onSessionExpired() { // If the session has been used recently, schedule a new expiry const remainder = Math.floor(this._sessionExpiry - (Date.now() / 1000)); if (remainder > 0) { this._sessionExpiryId = GLib.timeout_add_seconds( GLib.PRIORITY_DEFAULT, remainder, this._onSessionExpired.bind(this) ); return GLib.SOURCE_REMOVE; } // Otherwise if there's an active session, close it if (this._session !== null) this._session.stop(); // Reset the GSource Id this._sessionExpiryId = 0; return GLib.SOURCE_REMOVE; } _createRemoteDesktopSession() { if (this.connection === null) return Promise.reject(new Error('No DBus connection')); return new Promise((resolve, reject) => { this.connection.call( 'org.gnome.Mutter.RemoteDesktop', '/org/gnome/Mutter/RemoteDesktop', 'org.gnome.Mutter.RemoteDesktop', 'CreateSession', null, null, Gio.DBusCallFlags.NONE, -1, null, (connection, res) => { try { res = connection.call_finish(res); resolve(res.deepUnpack()[0]); } catch (e) { reject(e); } } ); }); } _createScreenCastSession(sessionId) { if (this.connection === null) return Promise.reject(new Error('No DBus connection')); return new Promise((resolve, reject) => { const options = new GLib.Variant('(a{sv})', [{ 'disable-animations': GLib.Variant.new_boolean(false), 'remote-desktop-session-id': GLib.Variant.new_string(sessionId), }]); this.connection.call( 'org.gnome.Mutter.ScreenCast', '/org/gnome/Mutter/ScreenCast', 'org.gnome.Mutter.ScreenCast', 'CreateSession', options, null, Gio.DBusCallFlags.NONE, -1, null, (connection, res) => { try { res = connection.call_finish(res); resolve(res.deepUnpack()[0]); } catch (e) { reject(e); } } ); }); } async _ensureAdapter() { try { // Update the timestamp of the last event this._sessionExpiry = Math.floor((Date.now() / 1000) + SESSION_TIMEOUT); // Session is active if (this._session !== null) return; // Mutter's RemoteDesktop is not available, fall back to Atspi if (this.connection === null) { debug('Falling back to Atspi'); // If we got here in Wayland, we need to re-adjust and bail if (this._checkWayland()) return; const fallback = imports.service.components.atspi; this._session = new fallback.Controller(); // Mutter is available and there isn't another session starting } else if (this._sessionStarting === false) { this._sessionStarting = true; debug('Creating Mutter RemoteDesktop session'); // This takes three steps: creating the remote desktop session, // starting the session, and creating a screencast session for // the remote desktop session. const objectPath = await this._createRemoteDesktopSession(); this._session = new RemoteSession(objectPath); await this._session.start(); await this._createScreenCastSession(this._session.session_id); // Watch for the session ending this._sessionClosedId = this._session.connect( 'closed', this._onSessionClosed.bind(this) ); if (this._sessionExpiryId === 0) { this._sessionExpiryId = GLib.timeout_add_seconds( GLib.PRIORITY_DEFAULT, SESSION_TIMEOUT, this._onSessionExpired.bind(this) ); } this._sessionStarting = false; } } catch (e) { logError(e); if (this._session !== null) { this._session.destroy(); this._session = null; } this._sessionStarting = false; } } /* * Pointer Events */ movePointer(dx, dy) { try { if (dx === 0 && dy === 0) return; this._ensureAdapter(); this._session.movePointer(dx, dy); } catch (e) { debug(e); } } pressPointer(button) { try { this._ensureAdapter(); this._session.pressPointer(button); } catch (e) { debug(e); } } releasePointer(button) { try { this._ensureAdapter(); this._session.releasePointer(button); } catch (e) { debug(e); } } clickPointer(button) { try { this._ensureAdapter(); this._session.clickPointer(button); } catch (e) { debug(e); } } doubleclickPointer(button) { try { this._ensureAdapter(); this._session.doubleclickPointer(button); } catch (e) { debug(e); } } scrollPointer(dx, dy) { if (dx === 0 && dy === 0) return; try { this._ensureAdapter(); this._session.scrollPointer(dx, dy); } catch (e) { debug(e); } } /* * Keyboard Events */ pressKeysym(keysym) { try { this._ensureAdapter(); this._session.pressKeysym(keysym); } catch (e) { debug(e); } } releaseKeysym(keysym) { try { this._ensureAdapter(); this._session.releaseKeysym(keysym); } catch (e) { debug(e); } } pressreleaseKeysym(keysym) { try { this._ensureAdapter(); this._session.pressreleaseKeysym(keysym); } catch (e) { debug(e); } } /* * High-level keyboard input */ pressKeys(input, modifiers) { try { this._ensureAdapter(); for (let i = 0; i < input.length; i++) this._session.pressKey(input[i], modifiers); } catch (e) { debug(e); } } destroy() { if (this._session !== null) { // Disconnect from the session if (this._sessionClosedId > 0) { this._session.disconnect(this._sessionClosedId); this._sessionClosedId = 0; } this._session.destroy(); this._session = null; } if (this._nameWatcherId > 0) { Gio.bus_unwatch_name(this._nameWatcherId); this._nameWatcherId = 0; } } } /** * The service class for this component */ var Component = Controller; gnome-shell-extension-gsconnect-50/src/service/components/mpris.js000066400000000000000000000633611421543444100256240ustar00rootroot00000000000000'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; var Player = GObject.registerClass({ GTypeName: 'GSConnectMediaPlayerInterface', Properties: { // Application Properties 'CanQuit': GObject.ParamSpec.boolean( 'CanQuit', 'Can Quit', 'Whether the client can call the Quit method.', GObject.ParamFlags.READABLE, false ), 'Fullscreen': GObject.ParamSpec.boolean( 'Fullscreen', 'Fullscreen', 'Whether the player is in fullscreen mode.', GObject.ParamFlags.READWRITE, false ), 'CanSetFullscreen': GObject.ParamSpec.boolean( 'CanSetFullscreen', 'Can Set Fullscreen', 'Whether the client can set the Fullscreen property.', GObject.ParamFlags.READABLE, false ), 'CanRaise': GObject.ParamSpec.boolean( 'CanRaise', 'Can Raise', 'Whether the client can call the Raise method.', GObject.ParamFlags.READABLE, false ), 'HasTrackList': GObject.ParamSpec.boolean( 'HasTrackList', 'Has Track List', 'Whether the player has a track list.', GObject.ParamFlags.READABLE, false ), 'Identity': GObject.ParamSpec.string( 'Identity', 'Identity', 'The application name.', GObject.ParamFlags.READABLE, null ), 'DesktopEntry': GObject.ParamSpec.string( 'DesktopEntry', 'DesktopEntry', 'The basename of an installed .desktop file.', GObject.ParamFlags.READABLE, null ), 'SupportedUriSchemes': GObject.param_spec_variant( 'SupportedUriSchemes', 'Supported URI Schemes', 'The URI schemes supported by the media player.', new GLib.VariantType('as'), null, GObject.ParamFlags.READABLE ), 'SupportedMimeTypes': GObject.param_spec_variant( 'SupportedMimeTypes', 'Supported MIME Types', 'The mime-types supported by the media player.', new GLib.VariantType('as'), null, GObject.ParamFlags.READABLE ), // Player Properties 'PlaybackStatus': GObject.ParamSpec.string( 'PlaybackStatus', 'Playback Status', 'The current playback status.', GObject.ParamFlags.READABLE, null ), 'LoopStatus': GObject.ParamSpec.string( 'LoopStatus', 'Loop Status', 'The current loop status.', GObject.ParamFlags.READWRITE, null ), 'Rate': GObject.ParamSpec.double( 'Rate', 'Rate', 'The current playback rate.', GObject.ParamFlags.READWRITE, 0.0, 1.0, 1.0 ), 'MinimumRate': GObject.ParamSpec.double( 'MinimumRate', 'Minimum Rate', 'The minimum playback rate.', GObject.ParamFlags.READWRITE, 0.0, 1.0, 1.0 ), 'MaximimRate': GObject.ParamSpec.double( 'MaximumRate', 'Maximum Rate', 'The maximum playback rate.', GObject.ParamFlags.READWRITE, 0.0, 1.0, 1.0 ), 'Shuffle': GObject.ParamSpec.boolean( 'Shuffle', 'Shuffle', 'Whether track changes are linear.', GObject.ParamFlags.READWRITE, null ), 'Metadata': GObject.param_spec_variant( 'Metadata', 'Metadata', 'The metadata of the current element.', new GLib.VariantType('a{sv}'), null, GObject.ParamFlags.READABLE ), 'Volume': GObject.ParamSpec.double( 'Volume', 'Volume', 'The volume level.', GObject.ParamFlags.READWRITE, 0.0, 1.0, 1.0 ), 'Position': GObject.ParamSpec.int64( 'Position', 'Position', 'The current track position in microseconds.', GObject.ParamFlags.READABLE, 0, Number.MAX_SAFE_INTEGER, 0 ), 'CanGoNext': GObject.ParamSpec.boolean( 'CanGoNext', 'Can Go Next', 'Whether the client can call the Next method.', GObject.ParamFlags.READABLE, false ), 'CanGoPrevious': GObject.ParamSpec.boolean( 'CanGoPrevious', 'Can Go Previous', 'Whether the client can call the Previous method.', GObject.ParamFlags.READABLE, false ), 'CanPlay': GObject.ParamSpec.boolean( 'CanPlay', 'Can Play', 'Whether playback can be started using Play or PlayPause.', GObject.ParamFlags.READABLE, false ), 'CanPause': GObject.ParamSpec.boolean( 'CanPause', 'Can Pause', 'Whether playback can be paused using Play or PlayPause.', GObject.ParamFlags.READABLE, false ), 'CanSeek': GObject.ParamSpec.boolean( 'CanSeek', 'Can Seek', 'Whether the client can control the playback position using Seek and SetPosition.', GObject.ParamFlags.READABLE, false ), 'CanControl': GObject.ParamSpec.boolean( 'CanControl', 'Can Control', 'Whether the media player may be controlled over this interface.', GObject.ParamFlags.READABLE, false ), }, Signals: { 'Seeked': { flags: GObject.SignalFlags.RUN_FIRST, param_types: [GObject.TYPE_INT64], }, }, }, class Player extends GObject.Object { /* * The org.mpris.MediaPlayer2 Interface */ get CanQuit() { if (this._CanQuit === undefined) this._CanQuit = false; return this._CanQuit; } get CanRaise() { if (this._CanRaise === undefined) this._CanRaise = false; return this._CanRaise; } get CanSetFullscreen() { if (this._CanFullscreen === undefined) this._CanFullscreen = false; return this._CanFullscreen; } get DesktopEntry() { if (this._DesktopEntry === undefined) return 'org.gnome.Shell.Extensions.GSConnect'; return this._DesktopEntry; } get Fullscreen() { if (this._Fullscreen === undefined) this._Fullscreen = false; return this._Fullscreen; } set Fullscreen(mode) { if (this.Fullscreen === mode) return; this._Fullscreen = mode; this.notify('Fullscreen'); } get HasTrackList() { if (this._HasTrackList === undefined) this._HasTrackList = false; return this._HasTrackList; } get Identity() { if (this._Identity === undefined) this._Identity = ''; return this._Identity; } get SupportedMimeTypes() { if (this._SupportedMimeTypes === undefined) this._SupportedMimeTypes = []; return this._SupportedMimeTypes; } get SupportedUriSchemes() { if (this._SupportedUriSchemes === undefined) this._SupportedUriSchemes = []; return this._SupportedUriSchemes; } Quit() { throw new GObject.NotImplementedError(); } Raise() { throw new GObject.NotImplementedError(); } /* * The org.mpris.MediaPlayer2.Player Interface */ get CanControl() { if (this._CanControl === undefined) this._CanControl = false; return this._CanControl; } get CanGoNext() { if (this._CanGoNext === undefined) this._CanGoNext = false; return this._CanGoNext; } get CanGoPrevious() { if (this._CanGoPrevious === undefined) this._CanGoPrevious = false; return this._CanGoPrevious; } get CanPause() { if (this._CanPause === undefined) this._CanPause = false; return this._CanPause; } get CanPlay() { if (this._CanPlay === undefined) this._CanPlay = false; return this._CanPlay; } get CanSeek() { if (this._CanSeek === undefined) this._CanSeek = false; return this._CanSeek; } get LoopStatus() { if (this._LoopStatus === undefined) this._LoopStatus = 'None'; return this._LoopStatus; } set LoopStatus(status) { if (this.LoopStatus === status) return; this._LoopStatus = status; this.notify('LoopStatus'); } get MaximumRate() { if (this._MaximumRate === undefined) this._MaximumRate = 1.0; return this._MaximumRate; } get Metadata() { if (this._Metadata === undefined) { this._Metadata = { 'xesam:artist': [_('Unknown')], 'xesam:album': _('Unknown'), 'xesam:title': _('Unknown'), 'mpris:length': 0, }; } return this._Metadata; } get MinimumRate() { if (this._MinimumRate === undefined) this._MinimumRate = 1.0; return this._MinimumRate; } get PlaybackStatus() { if (this._PlaybackStatus === undefined) this._PlaybackStatus = 'Stopped'; return this._PlaybackStatus; } get Position() { if (this._Position === undefined) this._Position = 0; return this._Position; } get Rate() { if (this._Rate === undefined) this._Rate = 1.0; return this._Rate; } set Rate(rate) { if (this.Rate === rate) return; this._Rate = rate; this.notify('Rate'); } get Shuffle() { if (this._Shuffle === undefined) this._Shuffle = false; return this._Shuffle; } set Shuffle(mode) { if (this.Shuffle === mode) return; this._Shuffle = mode; this.notify('Shuffle'); } get Volume() { if (this._Volume === undefined) this._Volume = 1.0; return this._Volume; } set Volume(level) { if (this.Volume === level) return; this._Volume = level; this.notify('Volume'); } Next() { throw new GObject.NotImplementedError(); } OpenUri(uri) { throw new GObject.NotImplementedError(); } Previous() { throw new GObject.NotImplementedError(); } Pause() { throw new GObject.NotImplementedError(); } Play() { throw new GObject.NotImplementedError(); } PlayPause() { throw new GObject.NotImplementedError(); } Seek(offset) { throw new GObject.NotImplementedError(); } SetPosition(trackId, position) { throw new GObject.NotImplementedError(); } Stop() { throw new GObject.NotImplementedError(); } }); /** * An aggregate of the org.mpris.MediaPlayer2 and org.mpris.MediaPlayer2.Player * interfaces. */ const PlayerProxy = GObject.registerClass({ GTypeName: 'GSConnectMPRISPlayer', }, class PlayerProxy extends Player { _init(name) { super._init(); this._application = new Gio.DBusProxy({ g_bus_type: Gio.BusType.SESSION, g_name: name, g_object_path: '/org/mpris/MediaPlayer2', g_interface_name: 'org.mpris.MediaPlayer2', }); this._applicationChangedId = this._application.connect( 'g-properties-changed', this._onPropertiesChanged.bind(this) ); this._player = new Gio.DBusProxy({ g_bus_type: Gio.BusType.SESSION, g_name: name, g_object_path: '/org/mpris/MediaPlayer2', g_interface_name: 'org.mpris.MediaPlayer2.Player', }); this._playerChangedId = this._player.connect( 'g-properties-changed', this._onPropertiesChanged.bind(this) ); this._playerSignalId = this._player.connect( 'g-signal', this._onSignal.bind(this) ); this._cancellable = new Gio.Cancellable(); } _onSignal(proxy, sender_name, signal_name, parameters) { try { if (signal_name !== 'Seeked') return; this.emit('Seeked', parameters.deepUnpack()[0]); } catch (e) { debug(e, proxy.g_name); } } _call(proxy, name, parameters = null) { proxy.call( name, parameters, Gio.DBusCallFlags.NO_AUTO_START, -1, this._cancellable, (proxy, result) => { try { proxy.call_finish(result); } catch (e) { Gio.DBusError.strip_remote_error(e); debug(e, proxy.g_name); } } ); } _get(proxy, name, fallback = null) { try { return proxy.get_cached_property(name).recursiveUnpack(); } catch (e) { return fallback; } } _set(proxy, name, value) { try { proxy.set_cached_property(name, value); proxy.call( 'org.freedesktop.DBus.Properties.Set', new GLib.Variant('(ssv)', [proxy.g_interface_name, name, value]), Gio.DBusCallFlags.NO_AUTO_START, -1, this._cancellable, (proxy, result) => { try { proxy.call_finish(result); } catch (e) { Gio.DBusError.strip_remote_error(e); debug(e, proxy.g_name); } } ); } catch (e) { debug(e, proxy.g_name); } } _onPropertiesChanged(proxy, changed, invalidated) { try { this.freeze_notify(); for (const name in changed.deepUnpack()) this.notify(name); this.thaw_notify(); } catch (e) { debug(e, proxy.g_name); } } initPromise() { const application = new Promise((resolve, reject) => { this._application.init_async(0, this._cancellable, (proxy, res) => { try { resolve(proxy.init_finish(res)); } catch (e) { reject(e); } }); }); const player = new Promise((resolve, reject) => { this._player.init_async(0, this._cancellable, (proxy, res) => { try { resolve(proxy.init_finish(res)); } catch (e) { reject(e); } }); }); return Promise.all([application, player]); } /* * The org.mpris.MediaPlayer2 Interface */ get CanQuit() { return this._get(this._application, 'CanQuit', false); } get CanRaise() { return this._get(this._application, 'CanRaise', false); } get CanSetFullscreen() { return this._get(this._application, 'CanSetFullscreen', false); } get DesktopEntry() { return this._get(this._application, 'DesktopEntry', null); } get Fullscreen() { return this._get(this._application, 'Fullscreen', false); } set Fullscreen(mode) { this._set(this._application, 'Fullscreen', new GLib.Variant('b', mode)); } get HasTrackList() { return this._get(this._application, 'HasTrackList', false); } get Identity() { return this._get(this._application, 'Identity', _('Unknown')); } get SupportedMimeTypes() { return this._get(this._application, 'SupportedMimeTypes', []); } get SupportedUriSchemes() { return this._get(this._application, 'SupportedUriSchemes', []); } Quit() { this._call(this._application, 'Quit'); } Raise() { this._call(this._application, 'Raise'); } /* * The org.mpris.MediaPlayer2.Player Interface */ get CanControl() { return this._get(this._player, 'CanControl', false); } get CanGoNext() { return this._get(this._player, 'CanGoNext', false); } get CanGoPrevious() { return this._get(this._player, 'CanGoPrevious', false); } get CanPause() { return this._get(this._player, 'CanPause', false); } get CanPlay() { return this._get(this._player, 'CanPlay', false); } get CanSeek() { return this._get(this._player, 'CanSeek', false); } get LoopStatus() { return this._get(this._player, 'LoopStatus', 'None'); } set LoopStatus(status) { this._set(this._player, 'LoopStatus', new GLib.Variant('s', status)); } get MaximumRate() { return this._get(this._player, 'MaximumRate', 1.0); } get Metadata() { if (this._metadata === undefined) { this._metadata = { 'xesam:artist': [_('Unknown')], 'xesam:album': _('Unknown'), 'xesam:title': _('Unknown'), 'mpris:length': 0, }; } return this._get(this._player, 'Metadata', this._metadata); } get MinimumRate() { return this._get(this._player, 'MinimumRate', 1.0); } get PlaybackStatus() { return this._get(this._player, 'PlaybackStatus', 'Stopped'); } // g-properties-changed is not emitted for this property get Position() { try { const reply = this._player.call_sync( 'org.freedesktop.DBus.Properties.Get', new GLib.Variant('(ss)', [ 'org.mpris.MediaPlayer2.Player', 'Position', ]), Gio.DBusCallFlags.NONE, -1, null ); return reply.recursiveUnpack()[0]; } catch (e) { return 0; } } get Rate() { return this._get(this._player, 'Rate', 1.0); } set Rate(rate) { this._set(this._player, 'Rate', new GLib.Variant('d', rate)); } get Shuffle() { return this._get(this._player, 'Shuffle', false); } set Shuffle(mode) { this._set(this._player, 'Shuffle', new GLib.Variant('b', mode)); } get Volume() { return this._get(this._player, 'Volume', 1.0); } set Volume(level) { this._set(this._player, 'Volume', new GLib.Variant('d', level)); } Next() { this._call(this._player, 'Next'); } OpenUri(uri) { this._call(this._player, 'OpenUri', new GLib.Variant('(s)', [uri])); } Previous() { this._call(this._player, 'Previous'); } Pause() { this._call(this._player, 'Pause'); } Play() { this._call(this._player, 'Play'); } PlayPause() { this._call(this._player, 'PlayPause'); } Seek(offset) { this._call(this._player, 'Seek', new GLib.Variant('(x)', [offset])); } SetPosition(trackId, position) { this._call(this._player, 'SetPosition', new GLib.Variant('(ox)', [trackId, position])); } Stop() { this._call(this._player, 'Stop'); } destroy() { if (this._cancellable.is_cancelled()) return; this._cancellable.cancel(); this._application.disconnect(this._applicationChangedId); this._player.disconnect(this._playerChangedId); this._player.disconnect(this._playerSignalId); } }); /** * A manager for media players */ var Manager = GObject.registerClass({ GTypeName: 'GSConnectMPRISManager', Signals: { 'player-added': { param_types: [GObject.TYPE_OBJECT], }, 'player-removed': { param_types: [GObject.TYPE_OBJECT], }, 'player-changed': { param_types: [GObject.TYPE_OBJECT], }, 'player-seeked': { param_types: [GObject.TYPE_OBJECT, GObject.TYPE_INT64], }, }, }, class Manager extends GObject.Object { _init() { super._init(); // Asynchronous setup this._cancellable = new Gio.Cancellable(); this._connection = Gio.DBus.session; this._players = new Map(); this._paused = new Map(); this._nameOwnerChangedId = Gio.DBus.session.signal_subscribe( 'org.freedesktop.DBus', 'org.freedesktop.DBus', 'NameOwnerChanged', '/org/freedesktop/DBus', 'org.mpris.MediaPlayer2', Gio.DBusSignalFlags.MATCH_ARG0_NAMESPACE, this._onNameOwnerChanged.bind(this) ); this._loadPlayers(); } async _loadPlayers() { try { const names = await new Promise((resolve, reject) => { this._connection.call( 'org.freedesktop.DBus', '/org/freedesktop/DBus', 'org.freedesktop.DBus', 'ListNames', null, null, Gio.DBusCallFlags.NONE, -1, this._cancellable, (connection, res) => { try { res = connection.call_finish(res); resolve(res.deepUnpack()[0]); } catch (e) { reject(e); } } ); }); for (let i = 0, len = names.length; i < len; i++) { const name = names[i]; if (!name.startsWith('org.mpris.MediaPlayer2')) continue; if (!name.includes('GSConnect')) this._addPlayer(name); } } catch (e) { if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) logError(e); } } _onNameOwnerChanged(connection, sender, object, iface, signal, parameters) { const [name, oldOwner, newOwner] = parameters.deepUnpack(); if (name.includes('GSConnect')) return; if (newOwner.length) this._addPlayer(name); else if (oldOwner.length) this._removePlayer(name); } async _addPlayer(name) { try { if (!this._players.has(name)) { const player = new PlayerProxy(name); await player.initPromise(); player.connect('notify', (player) => this.emit('player-changed', player)); player.connect('Seeked', this.emit.bind(this, 'player-seeked')); this._players.set(name, player); this.emit('player-added', player); } } catch (e) { debug(e, name); } } _removePlayer(name) { try { const player = this._players.get(name); if (player !== undefined) { this._paused.delete(name); this._players.delete(name); this.emit('player-removed', player); player.destroy(); } } catch (e) { debug(e, name); } } /** * Check for a player by its Identity. * * @param {string} identity - A player name * @return {boolean} %true if the player was found */ hasPlayer(identity) { for (const player of this._players.values()) { if (player.Identity === identity) return true; } return false; } /** * Get a player by its Identity. * * @param {string} identity - A player name * @return {GSConnectMPRISPlayer|null} A player or %null */ getPlayer(identity) { for (const player of this._players.values()) { if (player.Identity === identity) return player; } return null; } /** * Get a list of player identities. * * @return {string[]} A list of player identities */ getIdentities() { const identities = []; for (const player of this._players.values()) { const identity = player.Identity; if (identity) identities.push(identity); } return identities; } /** * A convenience function for pausing all players currently playing. */ pauseAll() { for (const [name, player] of this._players) { if (player.PlaybackStatus === 'Playing' && player.CanPause) { player.Pause(); this._paused.set(name, player); } } } /** * A convenience function for restarting all players paused with pauseAll(). */ unpauseAll() { for (const player of this._paused.values()) { if (player.PlaybackStatus === 'Paused' && player.CanPlay) player.Play(); } this._paused.clear(); } destroy() { if (this._cancellable.is_cancelled()) return; this._cancellable.cancel(); this._connection.signal_unsubscribe(this._nameOwnerChangedId); this._paused.clear(); this._players.forEach(player => player.destroy()); this._players.clear(); } }); /** * The service class for this component */ var Component = Manager; gnome-shell-extension-gsconnect-50/src/service/components/notification.js000066400000000000000000000333471421543444100271610ustar00rootroot00000000000000'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GjsPrivate = imports.gi.GjsPrivate; const GObject = imports.gi.GObject; const DBus = imports.service.utils.dbus; const _nodeInfo = Gio.DBusNodeInfo.new_for_xml(` `); const FDO_IFACE = _nodeInfo.lookup_interface('org.freedesktop.Notifications'); const FDO_MATCH = "interface='org.freedesktop.Notifications',member='Notify',type='method_call'"; const GTK_IFACE = _nodeInfo.lookup_interface('org.gtk.Notifications'); const GTK_MATCH = "interface='org.gtk.Notifications',member='AddNotification',type='method_call'"; /** * A class for snooping Freedesktop (libnotify) and Gtk (GNotification) * notifications and forwarding them to supporting devices. */ const Listener = GObject.registerClass({ GTypeName: 'GSConnectNotificationListener', Signals: { 'notification-added': { flags: GObject.SignalFlags.RUN_LAST, param_types: [GLib.Variant.$gtype], }, }, }, class Listener extends GObject.Object { _init() { super._init(); // Respect desktop notification settings this._settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.notifications', }); // Watch for new application policies this._settingsId = this._settings.connect( 'changed::application-children', this._onSettingsChanged.bind(this) ); // Cache for appName->desktop-id lookups this._names = {}; // Asynchronous setup this._init_async(); } get applications() { if (this._applications === undefined) this._onSettingsChanged(); return this._applications; } /** * Update application notification settings */ _onSettingsChanged() { this._applications = {}; for (const app of this._settings.get_strv('application-children')) { const appSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.notifications.application', path: `/org/gnome/desktop/notifications/application/${app}/`, }); const appInfo = Gio.DesktopAppInfo.new( appSettings.get_string('application-id') ); if (appInfo !== null) this._applications[appInfo.get_name()] = appSettings; } } _listNames() { return new Promise((resolve, reject) => { this._session.call( 'org.freedesktop.DBus', '/org/freedesktop/DBus', 'org.freedesktop.DBus', 'ListNames', null, null, Gio.DBusCallFlags.NONE, -1, null, (connection, res) => { try { res = connection.call_finish(res); resolve(res.deepUnpack()[0]); } catch (e) { reject(e); } } ); }); } _getNameOwner(name) { return new Promise((resolve, reject) => { this._session.call( 'org.freedesktop.DBus', '/org/freedesktop/DBus', 'org.freedesktop.DBus', 'GetNameOwner', new GLib.Variant('(s)', [name]), null, Gio.DBusCallFlags.NONE, -1, null, (connection, res) => { try { res = connection.call_finish(res); resolve(res.deepUnpack()[0]); } catch (e) { reject(e); } } ); }); } /** * Try and find a well-known name for @sender on the session bus * * @param {string} sender - A DBus unique name (eg. :1.2282) * @param {string} appName - @appName passed to Notify() (Optional) * @return {string} A well-known name or %null */ async _getAppId(sender, appName) { try { // Get a list of well-known names, ignoring @sender const names = await this._listNames(); names.splice(names.indexOf(sender), 1); // Make a short list for substring matches (fractal/org.gnome.Fractal) const appLower = appName.toLowerCase(); const shortList = names.filter(name => { return name.toLowerCase().includes(appLower); }); // Run the short list first for (const name of shortList) { const nameOwner = await this._getNameOwner(name); if (nameOwner === sender) return name; names.splice(names.indexOf(name), 1); } // Run the full list for (const name of names) { const nameOwner = await this._getNameOwner(name); if (nameOwner === sender) return name; } return null; } catch (e) { debug(e); return null; } } /** * Try and find the application name for @sender * * @param {string} sender - A DBus unique name * @param {string} [appName] - `appName` supplied by Notify() * @return {string} A well-known name or %null */ async _getAppName(sender, appName = null) { // Check the cache first if (appName && this._names.hasOwnProperty(appName)) return this._names[appName]; try { const appId = await this._getAppId(sender, appName); const appInfo = Gio.DesktopAppInfo.new(`${appId}.desktop`); this._names[appName] = appInfo.get_name(); appName = appInfo.get_name(); } catch (e) { // Silence errors } return appName; } /** * Callback for AddNotification()/Notify() * * @param {DBus.Interface} iface - The DBus interface * @param {string} name - The DBus method name * @param {GLib.Variant} parameters - The method parameters * @param {Gio.DBusMethodInvocation} invocation - The method invocation info */ async _onHandleMethodCall(iface, name, parameters, invocation) { try { // Check if notifications are disabled in desktop settings if (!this._settings.get_boolean('show-banners')) return; parameters = parameters.full_unpack(); // GNotification if (name === 'AddNotification') { this.AddNotification(...parameters); // libnotify } else if (name === 'Notify') { const message = invocation.get_message(); if (this._fdoNameOwner === undefined) { this._fdoNameOwner = await this._getNameOwner( 'org.freedesktop.Notifications'); } if (this._fdoNameOwner !== message.get_destination()) return; // Try to brute-force an application name using DBus if (!this.applications.hasOwnProperty(parameters[0])) { const sender = message.get_sender(); parameters[0] = await this._getAppName(sender, parameters[0]); } this.Notify(...parameters); } } catch (e) { debug(e); } } /** * Export interfaces for proxying notifications and become a monitor * * @return {Promise} A promise for the operation */ _monitorConnection() { return new Promise((resolve, reject) => { // libnotify Interface this._fdoNotifications = new GjsPrivate.DBusImplementation({ g_interface_info: FDO_IFACE, }); this._fdoMethodCallId = this._fdoNotifications.connect( 'handle-method-call', this._onHandleMethodCall.bind(this) ); this._fdoNotifications.export( this._monitor, '/org/freedesktop/Notifications' ); this._fdoNameOwnerChangedId = this._session.signal_subscribe( 'org.freedesktop.DBus', 'org.freedesktop.DBus', 'NameOwnerChanged', '/org/freedesktop/DBus', 'org.freedesktop.Notifications', Gio.DBusSignalFlags.MATCH_ARG0_NAMESPACE, this._onFdoNameOwnerChanged.bind(this) ); // GNotification Interface this._gtkNotifications = new GjsPrivate.DBusImplementation({ g_interface_info: GTK_IFACE, }); this._gtkMethodCallId = this._gtkNotifications.connect( 'handle-method-call', this._onHandleMethodCall.bind(this) ); this._gtkNotifications.export( this._monitor, '/org/gtk/Notifications' ); // Become a monitor for Fdo & Gtk notifications this._monitor.call( 'org.freedesktop.DBus', '/org/freedesktop/DBus', 'org.freedesktop.DBus.Monitoring', 'BecomeMonitor', new GLib.Variant('(asu)', [[FDO_MATCH, GTK_MATCH], 0]), null, Gio.DBusCallFlags.NONE, -1, null, (connection, res) => { try { resolve(connection.call_finish(res)); } catch (e) { reject(e); } } ); }); } async _init_async() { try { this._session = await DBus.getConnection(); this._monitor = await DBus.newConnection(); await this._monitorConnection(); } catch (e) { const service = Gio.Application.get_default(); if (service !== null) service.notify_error(e); else logError(e); } } _onFdoNameOwnerChanged(connection, sender, object, iface, signal, parameters) { this._fdoNameOwner = parameters.deepUnpack()[2]; } _sendNotification(notif) { // Check if this application is disabled in desktop settings const appSettings = this.applications[notif.appName]; if (appSettings && !appSettings.get_boolean('enable')) return; // Send the notification to each supporting device // TODO: avoid the overhead of the GAction framework with a signal? const variant = GLib.Variant.full_pack(notif); this.emit('notification-added', variant); } Notify(appName, replacesId, iconName, summary, body, actions, hints, timeout) { // Ignore notifications without an appName if (!appName) return; this._sendNotification({ appName: appName, id: `fdo|null|${replacesId}`, title: summary, text: body, ticker: `${summary}: ${body}`, isClearable: (replacesId !== 0), icon: iconName, }); } AddNotification(application, id, notification) { // Ignore our own notifications or we'll cause a notification loop if (application === 'org.gnome.Shell.Extensions.GSConnect') return; const appInfo = Gio.DesktopAppInfo.new(`${application}.desktop`); // Try to get an icon for the notification if (!notification.hasOwnProperty('icon')) notification.icon = appInfo.get_icon() || undefined; this._sendNotification({ appName: appInfo.get_name(), id: `gtk|${application}|${id}`, title: notification.title, text: notification.body, ticker: `${notification.title}: ${notification.body}`, isClearable: true, icon: notification.icon, }); } destroy() { try { if (this._fdoNotifications) { this._fdoNotifications.disconnect(this._fdoMethodCallId); this._fdoNotifications.unexport(); this._session.signal_unsubscribe(this._fdoNameOwnerChangedId); } if (this._gtkNotifications) { this._gtkNotifications.disconnect(this._gtkMethodCallId); this._gtkNotifications.unexport(); } if (this._settings) { this._settings.disconnect(this._settingsId); this._settings.run_dispose(); } // TODO: Gio.IOErrorEnum: The connection is closed // this._monitor.close_sync(null); GObject.signal_handlers_destroy(this); } catch (e) { debug(e); } } }); /** * The service class for this component */ var Component = Listener; gnome-shell-extension-gsconnect-50/src/service/components/pulseaudio.js000066400000000000000000000145321421543444100266400ustar00rootroot00000000000000'use strict'; const Tweener = imports.tweener.tweener; const GIRepository = imports.gi.GIRepository; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Config = imports.config; // Add gnome-shell's typelib dir to the search path const typelibDir = GLib.build_filenamev([Config.GNOME_SHELL_LIBDIR, 'gnome-shell']); GIRepository.Repository.prepend_search_path(typelibDir); GIRepository.Repository.prepend_library_path(typelibDir); const Gvc = imports.gi.Gvc; /** * Extend Gvc.MixerStream with a property for returning a user-visible name */ Object.defineProperty(Gvc.MixerStream.prototype, 'display_name', { get: function () { try { if (!this.get_ports().length) return this.description; return `${this.get_port().human_port} (${this.description})`; } catch (e) { return this.description; } }, }); /** * A convenience wrapper for Gvc.MixerStream */ class Stream { constructor(mixer, stream) { this._mixer = mixer; this._stream = stream; this._max = mixer.get_vol_max_norm(); } get muted() { return this._stream.is_muted; } set muted(bool) { this._stream.change_is_muted(bool); } // Volume is a double in the range 0-1 get volume() { return Math.floor(100 * this._stream.volume / this._max) / 100; } set volume(num) { this._stream.volume = Math.floor(num * this._max); this._stream.push_volume(); } /** * Gradually raise or lower the stream volume to @value * * @param {number} value - A number in the range 0-1 * @param {number} [duration] - Duration to fade in seconds */ fade(value, duration = 1) { Tweener.removeTweens(this); if (this._stream.volume > value) { this._mixer.fading = true; Tweener.addTween(this, { volume: value, time: duration, transition: 'easeOutCubic', onComplete: () => { this._mixer.fading = false; }, }); } else if (this._stream.volume < value) { this._mixer.fading = true; Tweener.addTween(this, { volume: value, time: duration, transition: 'easeInCubic', onComplete: () => { this._mixer.fading = false; }, }); } } } /** * A subclass of Gvc.MixerControl with convenience functions for controlling the * default input/output volumes. * * The Mixer class uses GNOME Shell's Gvc library to control the system volume * and offers a few convenience functions. */ const Mixer = GObject.registerClass({ GTypeName: 'GSConnectAudioMixer', }, class Mixer extends Gvc.MixerControl { _init(params) { super._init({name: 'GSConnect'}); this._previousVolume = undefined; this._volumeMuted = false; this._microphoneMuted = false; this.open(); } get fading() { if (this._fading === undefined) this._fading = false; return this._fading; } set fading(bool) { if (this.fading === bool) return; this._fading = bool; if (this.fading) this.emit('stream-changed', this._output._stream.id); } get input() { if (this._input === undefined) this.vfunc_default_source_changed(); return this._input; } get output() { if (this._output === undefined) this.vfunc_default_sink_changed(); return this._output; } vfunc_default_sink_changed(id) { try { const sink = this.get_default_sink(); this._output = (sink) ? new Stream(this, sink) : null; } catch (e) { logError(e); } } vfunc_default_source_changed(id) { try { const source = this.get_default_source(); this._input = (source) ? new Stream(this, source) : null; } catch (e) { logError(e); } } vfunc_state_changed(new_state) { try { if (new_state === Gvc.MixerControlState.READY) { this.vfunc_default_sink_changed(null); this.vfunc_default_source_changed(null); } } catch (e) { logError(e); } } /** * Store the current output volume then lower it to %15 * * @param {number} duration - Duration in seconds to fade */ lowerVolume(duration = 1) { try { if (this.output.volume > 0.15) { this._previousVolume = Number(this.output.volume); this.output.fade(0.15, duration); } } catch (e) { logError(e); } } /** * Mute the output volume (speakers) */ muteVolume() { try { if (this.output.muted) return; this.output.muted = true; this._volumeMuted = true; } catch (e) { logError(e); } } /** * Mute the input volume (microphone) */ muteMicrophone() { try { if (this.input.muted) return; this.input.muted = true; this._microphoneMuted = true; } catch (e) { logError(e); } } /** * Restore all mixer levels to their previous state */ restore() { try { // If we muted the microphone, unmute it before restoring the volume if (this._microphoneMuted) { this.input.muted = false; this._microphoneMuted = false; } // If we muted the volume, unmute it before restoring the volume if (this._volumeMuted) { this.output.muted = false; this._volumeMuted = false; } // If a previous volume is defined, raise it back up to that level if (this._previousVolume !== undefined) { this.output.fade(this._previousVolume); this._previousVolume = undefined; } } catch (e) { logError(e); } } destroy() { this.close(); } }); /** * The service class for this component */ var Component = Mixer; gnome-shell-extension-gsconnect-50/src/service/components/session.js000066400000000000000000000056411421543444100261520ustar00rootroot00000000000000'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Session = class { constructor() { this._connection = Gio.DBus.system; this._session = null; this._initAsync(); } async _initAsync() { try { const userName = GLib.get_user_name(); const sessions = await this._listSessions(); let sessionPath = '/org/freedesktop/login1/session/auto'; // eslint-disable-next-line no-unused-vars for (const [num, uid, name, seat, objectPath] of sessions) { if (name === userName) { sessionPath = objectPath; break; } } this._session = await this._getSession(sessionPath); } catch (e) { this._session = null; logError(e); } } get idle() { if (this._session === null) return false; return this._session.get_cached_property('IdleHint').unpack(); } get locked() { if (this._session === null) return false; return this._session.get_cached_property('LockedHint').unpack(); } get active() { // Active if not idle and not locked return !(this.idle || this.locked); } _listSessions() { return new Promise((resolve, reject) => { this._connection.call( 'org.freedesktop.login1', '/org/freedesktop/login1', 'org.freedesktop.login1.Manager', 'ListSessions', null, null, Gio.DBusCallFlags.NONE, -1, null, (connection, res) => { try { res = connection.call_finish(res); resolve(res.deepUnpack()[0]); } catch (e) { reject(e); } } ); }); } async _getSession(objectPath) { const session = new Gio.DBusProxy({ g_connection: this._connection, g_name: 'org.freedesktop.login1', g_object_path: objectPath, g_interface_name: 'org.freedesktop.login1.Session', }); // Initialize the proxy await new Promise((resolve, reject) => { session.init_async( GLib.PRIORITY_DEFAULT, null, (proxy, res) => { try { resolve(proxy.init_finish(res)); } catch (e) { Gio.DBusError.strip_remote_error(e); reject(e); } } ); }); return session; } destroy() { this._session = null; } }; /** * The service class for this component */ var Component = Session; gnome-shell-extension-gsconnect-50/src/service/components/sound.js000066400000000000000000000120421421543444100256100ustar00rootroot00000000000000'use strict'; const Gdk = imports.gi.Gdk; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; /* * Used to ensure 'audible-bell' is enabled for fallback */ const WM_SETTINGS = new Gio.Settings({ schema_id: 'org.gnome.desktop.wm.preferences', path: '/org/gnome/desktop/wm/preferences/', }); var Player = class Player { constructor() { this._playing = new Set(); } get backend() { if (this._backend === undefined) { // Prefer GSound try { this._gsound = new imports.gi.GSound.Context(); this._gsound.init(null); this._backend = 'gsound'; // Try falling back to libcanberra, otherwise just re-run the test // in case one or the other is installed later } catch (e) { if (GLib.find_program_in_path('canberra-gtk-play') !== null) { this._canberra = new Gio.SubprocessLauncher({ flags: Gio.SubprocessFlags.NONE, }); this._backend = 'libcanberra'; } else { return null; } } } return this._backend; } _canberraPlaySound(name, cancellable) { return new Promise((resolve, reject) => { const proc = this._canberra.spawnv(['canberra-gtk-play', '-i', name]); proc.wait_check_async(cancellable, (proc, res) => { try { resolve(proc.wait_check_finish(res)); } catch (e) { reject(e); } }); }); } async _canberraLoopSound(name, cancellable) { while (!cancellable.is_cancelled()) await this._canberraPlaySound(name, cancellable); } _gsoundPlaySound(name, cancellable) { return new Promise((resolve, reject) => { this._gsound.play_full( {'event.id': name}, cancellable, (source, res) => { try { resolve(source.play_full_finish(res)); } catch (e) { reject(e); } } ); }); } async _gsoundLoopSound(name, cancellable) { while (!cancellable.is_cancelled()) await this._gsoundPlaySound(name, cancellable); } _gdkPlaySound(name, cancellable) { if (this._display === undefined) this._display = Gdk.Display.get_default(); let count = 0; GLib.timeout_add(GLib.PRIORITY_DEFAULT, 200, () => { try { if (count++ < 4 && !cancellable.is_cancelled()) { this._display.beep(); return GLib.SOURCE_CONTINUE; } return GLib.SOURCE_REMOVE; } catch (e) { logError(e); return GLib.SOURCE_REMOVE; } }); return !cancellable.is_cancelled(); } _gdkLoopSound(name, cancellable) { this._gdkPlaySound(name, cancellable); GLib.timeout_add( GLib.PRIORITY_DEFAULT, 1500, this._gdkPlaySound.bind(this, name, cancellable) ); } async playSound(name, cancellable) { try { if (!(cancellable instanceof Gio.Cancellable)) cancellable = new Gio.Cancellable(); this._playing.add(cancellable); switch (this.backend) { case 'gsound': await this._gsoundPlaySound(name, cancellable); break; case 'canberra': await this._canberraPlaySound(name, cancellable); break; default: await this._gdkPlaySound(name, cancellable); } } catch (e) { if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) logError(e); } finally { this._playing.delete(cancellable); } } async loopSound(name, cancellable) { try { if (!(cancellable instanceof Gio.Cancellable)) cancellable = new Gio.Cancellable(); this._playing.add(cancellable); switch (this.backend) { case 'gsound': await this._gsoundLoopSound(name, cancellable); break; case 'canberra': await this._canberraLoopSound(name, cancellable); break; default: await this._gdkLoopSound(name, cancellable); } } catch (e) { if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) logError(e); } finally { this._playing.delete(cancellable); } } destroy() { for (const cancellable of this._playing) cancellable.cancel(); } }; /** * The service class for this component */ var Component = Player; gnome-shell-extension-gsconnect-50/src/service/components/upower.js000066400000000000000000000127511421543444100260100ustar00rootroot00000000000000'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; /** * The warning level of a battery. * * @readonly * @enum {number} */ const DeviceLevel = { UNKNOWN: 0, NONE: 1, DISCHARGING: 2, LOW: 3, CRITICAL: 4, ACTION: 5, NORMAL: 6, HIGH: 7, FULL: 8, LAST: 9, }; /** * The device state. * * @readonly * @enum {number} */ const DeviceState = { UNKNOWN: 0, CHARGING: 1, DISCHARGING: 2, EMPTY: 3, FULLY_CHARGED: 4, PENDING_CHARGE: 5, PENDING_DISCHARGE: 6, LAST: 7, }; /** * A class representing the system battery. */ var Battery = GObject.registerClass({ GTypeName: 'GSConnectSystemBattery', Signals: { 'changed': { flags: GObject.SignalFlags.RUN_FIRST, }, }, Properties: { 'charging': GObject.ParamSpec.boolean( 'charging', 'Charging', 'The current charging state.', GObject.ParamFlags.READABLE, false ), 'level': GObject.ParamSpec.int( 'level', 'Level', 'The current power level.', GObject.ParamFlags.READABLE, -1, 100, -1 ), 'threshold': GObject.ParamSpec.uint( 'threshold', 'Threshold', 'The current threshold state.', GObject.ParamFlags.READABLE, 0, 1, 0 ), }, }, class Battery extends GObject.Object { _init() { super._init(); this._cancellable = new Gio.Cancellable(); this._proxy = null; this._propertiesChangedId = 0; this._loadUPower(); } async _loadUPower() { try { this._proxy = new Gio.DBusProxy({ g_bus_type: Gio.BusType.SYSTEM, g_name: 'org.freedesktop.UPower', g_object_path: '/org/freedesktop/UPower/devices/DisplayDevice', g_interface_name: 'org.freedesktop.UPower.Device', g_flags: Gio.DBusProxyFlags.DO_NOT_AUTO_START, }); await new Promise((resolve, reject) => { this._proxy.init_async( GLib.PRIORITY_DEFAULT, this._cancellable, (proxy, res) => { try { resolve(proxy.init_finish(res)); } catch (e) { reject(e); } } ); }); this._propertiesChangedId = this._proxy.connect( 'g-properties-changed', this._onPropertiesChanged.bind(this) ); this._initProperties(this._proxy); } catch (e) { if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) { const service = Gio.Application.get_default(); if (service !== null) service.notify_error(e); else logError(e); } this._proxy = null; } } _initProperties(proxy) { if (proxy.g_name_owner === null) return; const percentage = proxy.get_cached_property('Percentage').unpack(); const state = proxy.get_cached_property('State').unpack(); const level = proxy.get_cached_property('WarningLevel').unpack(); this._level = Math.floor(percentage); this._charging = (state !== DeviceState.DISCHARGING); this._threshold = (!this.charging && level >= DeviceLevel.LOW); this.emit('changed'); } _onPropertiesChanged(proxy, changed, invalidated) { let emitChanged = false; const properties = changed.deepUnpack(); if (properties.hasOwnProperty('Percentage')) { emitChanged = true; const value = proxy.get_cached_property('Percentage').unpack(); this._level = Math.floor(value); this.notify('level'); } if (properties.hasOwnProperty('State')) { emitChanged = true; const value = proxy.get_cached_property('State').unpack(); this._charging = (value !== DeviceState.DISCHARGING); this.notify('charging'); } if (properties.hasOwnProperty('WarningLevel')) { emitChanged = true; const value = proxy.get_cached_property('WarningLevel').unpack(); this._threshold = (!this.charging && value >= DeviceLevel.LOW); this.notify('threshold'); } if (emitChanged) this.emit('changed'); } get charging() { if (this._charging === undefined) this._charging = false; return this._charging; } get is_present() { return (this._proxy && this._proxy.g_name_owner); } get level() { if (this._level === undefined) this._level = -1; return this._level; } get threshold() { if (this._threshold === undefined) this._threshold = 0; return this._threshold; } destroy() { if (this._cancellable.is_cancelled()) return; this._cancellable.cancel(); if (this._proxy && this._propertiesChangedId > 0) { this._proxy.disconnect(this._propertiesChangedId); this._propertiesChangedId = 0; } } }); /** * The service class for this component */ var Component = Battery; gnome-shell-extension-gsconnect-50/src/service/core.js000066400000000000000000000464741421543444100232430ustar00rootroot00000000000000'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; /** * Get the local device type. * * @return {string} A device type string */ function _getDeviceType() { try { let type = GLib.file_get_contents('/sys/class/dmi/id/chassis_type')[1]; type = Number(imports.byteArray.toString(type)); if ([8, 9, 10, 14].includes(type)) return 'laptop'; return 'desktop'; } catch (e) { return 'desktop'; } } /** * The packet class is a simple Object-derived class, offering some conveniences * for working with KDE Connect packets. */ var Packet = class Packet { constructor(data = null) { this.id = 0; this.type = undefined; this.body = {}; if (typeof data === 'string') Object.assign(this, JSON.parse(data)); else if (data !== null) Object.assign(this, data); } [Symbol.toPrimitive](hint) { this.id = Date.now(); if (hint === 'string') return `${JSON.stringify(this)}\n`; if (hint === 'number') return `${JSON.stringify(this)}\n`.length; return true; } get [Symbol.toStringTag]() { return `Packet:${this.type}`; } /** * Deserialize and return a new Packet from an Object or string. * * @param {Object|string} data - A string or dictionary to deserialize * @return {Core.Packet} A new packet object */ static deserialize(data) { return new Packet(data); } /** * Serialize the packet as a single line with a terminating new-line (`\n`) * character, ready to be written to a channel. * * @return {string} A serialized packet */ serialize() { this.id = Date.now(); return `${JSON.stringify(this)}\n`; } /** * Update the packet from a dictionary or string of JSON * * @param {Object|string} source - Source data */ update(source) { try { if (typeof data === 'string') Object.assign(this, JSON.parse(source)); else Object.assign(this, source); } catch (e) { throw Error(`Malformed data: ${e.message}`); } } /** * Check if the packet has a payload. * * @return {boolean} %true if @packet has a payload */ hasPayload() { if (!this.hasOwnProperty('payloadSize')) return false; if (!this.hasOwnProperty('payloadTransferInfo')) return false; return (Object.keys(this.payloadTransferInfo).length > 0); } }; /** * Channel objects handle KDE Connect packet exchange and data transfers for * devices. The implementation is responsible for all negotiation of the * underlying protocol. */ var Channel = GObject.registerClass({ GTypeName: 'GSConnectChannel', Properties: { 'closed': GObject.ParamSpec.boolean( 'closed', 'Closed', 'Whether the channel has been closed', GObject.ParamFlags.READABLE, false ), }, }, class Channel extends GObject.Object { get address() { throw new GObject.NotImplementedError(); } get backend() { if (this._backend === undefined) this._backend = null; return this._backend; } set backend(backend) { this._backend = backend; } get cancellable() { if (this._cancellable === undefined) this._cancellable = new Gio.Cancellable(); return this._cancellable; } get closed() { if (this._closed === undefined) this._closed = false; return this._closed; } get input_stream() { if (this._input_stream === undefined) { if (this._connection instanceof Gio.IOStream) return this._connection.get_input_stream(); return null; } return this._input_stream; } set input_stream(stream) { this._input_stream = stream; } get output_stream() { if (this._output_stream === undefined) { if (this._connection instanceof Gio.IOStream) return this._connection.get_output_stream(); return null; } return this._output_stream; } set output_stream(stream) { this._output_stream = stream; } get uuid() { if (this._uuid === undefined) this._uuid = GLib.uuid_string_random(); return this._uuid; } set uuid(uuid) { this._uuid = uuid; } /** * Close the channel. */ close() { throw new GObject.NotImplementedError(); } /** * Read a packet. * * @param {Gio.Cancellable} [cancellable] - A cancellable * @return {Promise} The packet */ readPacket(cancellable = null) { if (cancellable === null) cancellable = this.cancellable; if (!(this.input_stream instanceof Gio.DataInputStream)) { this.input_stream = new Gio.DataInputStream({ base_stream: this.input_stream, }); } return new Promise((resolve, reject) => { this.input_stream.read_line_async( GLib.PRIORITY_DEFAULT, cancellable, (stream, res) => { try { const data = stream.read_line_finish_utf8(res)[0]; if (data === null) { throw new Gio.IOErrorEnum({ message: 'End of stream', code: Gio.IOErrorEnum.CONNECTION_CLOSED, }); } resolve(new Packet(data)); } catch (e) { reject(e); } } ); }); } /** * Send a packet. * * @param {Core.Packet} packet - The packet to send * @param {Gio.Cancellable} [cancellable] - A cancellable * @return {Promise} %true if successful */ sendPacket(packet, cancellable = null) { if (cancellable === null) cancellable = this.cancellable; return new Promise((resolve, reject) => { this.output_stream.write_all_async( packet.serialize(), GLib.PRIORITY_DEFAULT, cancellable, (stream, res) => { try { resolve(stream.write_all_finish(res)); } catch (e) { reject(e); } } ); }); } /** * Reject a transfer. * * @param {Core.Packet} packet - A packet with payload info */ rejectTransfer(packet) { throw new GObject.NotImplementedError(); } /** * Download a payload from a device. Typically implementations will override * this with an async function. * * @param {Core.Packet} packet - A packet * @param {Gio.OutputStream} target - The target stream * @param {Gio.Cancellable} [cancellable] - A cancellable for the upload */ download(packet, target, cancellable = null) { throw new GObject.NotImplementedError(); } /** * Upload a payload to a device. Typically implementations will override * this with an async function. * * @param {Core.Packet} packet - The packet describing the transfer * @param {Gio.InputStream} source - The source stream * @param {number} size - The payload size * @param {Gio.Cancellable} [cancellable] - A cancellable for the upload */ upload(packet, source, size, cancellable = null) { throw new GObject.NotImplementedError(); } }); /** * ChannelService implementations provide Channel objects, emitting the * ChannelService::channel signal when a new connection has been accepted. */ var ChannelService = GObject.registerClass({ GTypeName: 'GSConnectChannelService', Properties: { 'active': GObject.ParamSpec.boolean( 'active', 'Active', 'Whether the service is active', GObject.ParamFlags.READABLE, false ), 'id': GObject.ParamSpec.string( 'id', 'ID', 'The hostname or other network unique id', GObject.ParamFlags.READWRITE, null ), 'name': GObject.ParamSpec.string( 'name', 'Name', 'The name of the backend', GObject.ParamFlags.READWRITE, null ), }, Signals: { 'channel': { flags: GObject.SignalFlags.RUN_LAST, param_types: [Channel.$gtype], return_type: GObject.TYPE_BOOLEAN, }, }, }, class ChannelService extends GObject.Object { get active() { if (this._active === undefined) this._active = false; return this._active; } get name() { if (this._name === undefined) this._name = GLib.get_host_name(); return this._name; } set name(name) { if (this.name === name) return; this._name = name; this.notify('name'); } get id() { if (this._id === undefined) this._id = GLib.uuid_string_random(); return this._id; } set id(id) { if (this.id === id) return; this._id = id; } get identity() { if (this._identity === undefined) this.buildIdentity(); return this._identity; } /** * Broadcast directly to @address or the whole network if %null * * @param {string} [address] - A string address */ broadcast(address = null) { throw new GObject.NotImplementedError(); } /** * Rebuild the identity packet used to identify the local device. An * implementation may override this to make modifications to the default * capabilities if necessary (eg. bluez without SFTP support). */ buildIdentity() { this._identity = new Packet({ id: 0, type: 'kdeconnect.identity', body: { deviceId: this.id, deviceName: this.name, deviceType: _getDeviceType(), protocolVersion: 7, incomingCapabilities: [], outgoingCapabilities: [], }, }); for (const name in imports.service.plugins) { // Exclude mousepad/presenter capability in unsupported sessions if (!HAVE_REMOTEINPUT && ['mousepad', 'presenter'].includes(name)) continue; const meta = imports.service.plugins[name].Metadata; if (meta === undefined) continue; for (const type of meta.incomingCapabilities) this._identity.body.incomingCapabilities.push(type); for (const type of meta.outgoingCapabilities) this._identity.body.outgoingCapabilities.push(type); } } /** * Emit Core.ChannelService::channel * * @param {Core.Channel} channel - The new channel */ channel(channel) { if (!this.emit('channel', channel)) channel.close(); } /** * Start the channel service. Implementations should throw an error if the * service fails to meet any of its requirements for opening or accepting * connections. */ start() { throw new GObject.NotImplementedError(); } /** * Stop the channel service. */ stop() { throw new GObject.NotImplementedError(); } /** * Destroy the channel service. */ destroy() { } }); /** * A class representing a file transfer. */ var Transfer = GObject.registerClass({ GTypeName: 'GSConnectTransfer', Properties: { 'channel': GObject.ParamSpec.object( 'channel', 'Channel', 'The channel that owns this transfer', GObject.ParamFlags.READWRITE, Channel.$gtype ), 'completed': GObject.ParamSpec.boolean( 'completed', 'Completed', 'Whether the transfer has completed', GObject.ParamFlags.READABLE, false ), 'device': GObject.ParamSpec.object( 'device', 'Device', 'The device that created this transfer', GObject.ParamFlags.READWRITE, GObject.Object.$gtype ), }, }, class Transfer extends GObject.Object { _init(params = {}) { super._init(params); this._cancellable = new Gio.Cancellable(); this._items = []; } get channel() { if (this._channel === undefined) this._channel = null; return this._channel; } set channel(channel) { if (this.channel === channel) return; this._channel = channel; } get completed() { if (this._completed === undefined) this._completed = false; return this._completed; } get device() { if (this._device === undefined) this._device = null; return this._device; } set device(device) { if (this.device === device) return; this._device = device; } get uuid() { if (this._uuid === undefined) this._uuid = GLib.uuid_string_random(); return this._uuid; } /** * Ensure there is a stream for the transfer item. * * @param {Object} item - A transfer item * @param {Gio.Cancellable} [cancellable] - A cancellable */ async _ensureStream(item, cancellable = null) { // This is an upload from a remote device if (item.packet.hasPayload()) { if (item.target instanceof Gio.OutputStream) return; if (item.file instanceof Gio.File) { item.target = await new Promise((resolve, reject) => { item.file.replace_async( null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, GLib.PRIORITY_DEFAULT, this._cancellable, (file, res) => { try { resolve(file.replace_finish(res)); } catch (e) { reject(e); } } ); }); } } else { if (item.source instanceof Gio.InputStream) return; if (item.file instanceof Gio.File) { const read = new Promise((resolve, reject) => { item.file.read_async( GLib.PRIORITY_DEFAULT, cancellable, (file, res) => { try { resolve(file.read_finish(res)); } catch (e) { reject(e); } } ); }); const query = new Promise((resolve, reject) => { item.file.query_info_async( Gio.FILE_ATTRIBUTE_STANDARD_SIZE, Gio.FileQueryInfoFlags.NONE, GLib.PRIORITY_DEFAULT, cancellable, (file, res) => { try { resolve(file.query_info_finish(res)); } catch (e) { reject(e); } } ); }); const [stream, info] = await Promise.all([read, query]); item.source = stream; item.size = info.get_size(); } } } /** * Add a file to the transfer. * * @param {Core.Packet} packet - A packet * @param {Gio.File} file - A file to transfer */ addFile(packet, file) { const item = { packet: new Packet(packet), file: file, source: null, target: null, }; this._items.push(item); } /** * Add a filepath to the transfer. * * @param {Core.Packet} packet - A packet * @param {string} path - A filepath to transfer */ addPath(packet, path) { const item = { packet: new Packet(packet), file: Gio.File.new_for_path(path), source: null, target: null, }; this._items.push(item); } /** * Add a stream to the transfer. * * @param {Core.Packet} packet - A packet * @param {Gio.InputStream|Gio.OutputStream} stream - A stream to transfer * @param {number} [size] - Payload size */ addStream(packet, stream, size = 0) { const item = { packet: new Packet(packet), file: null, source: null, target: null, size: size, }; if (stream instanceof Gio.InputStream) item.source = stream; else if (stream instanceof Gio.OutputStream) item.target = stream; this._items.push(item); } /** * Execute a transfer operation. Implementations may override this, while * the default uses g_output_stream_splice(). * * @param {Gio.Cancellable} [cancellable] - A cancellable */ async start(cancellable = null) { let error = null; try { let item; // If a cancellable is passed in, chain to its signal if (cancellable instanceof Gio.Cancellable) cancellable.connect(() => this._cancellable.cancel()); while ((item = this._items.shift())) { // If created for a device, ignore connection changes by // ensuring we have the most recent channel if (this.device !== null) this._channel = this.device.channel; // TODO: transfer queueing? if (this.channel === null || this.channel.closed) { throw new Gio.IOErrorEnum({ code: Gio.IOErrorEnum.CONNECTION_CLOSED, message: 'Channel is closed', }); } await this._ensureStream(item, this._cancellable); if (item.packet.hasPayload()) { await this.channel.download(item.packet, item.target, this._cancellable); } else { await this.channel.upload(item.packet, item.source, item.size, this._cancellable); } } } catch (e) { error = e; } finally { this._completed = true; this.notify('completed'); } if (error !== null) throw error; } cancel() { if (this._cancellable.is_cancelled() === false) this._cancellable.cancel(); } }); gnome-shell-extension-gsconnect-50/src/service/daemon.js000077500000000000000000000501631421543444100235470ustar00rootroot00000000000000#!/usr/bin/env gjs 'use strict'; // Allow TLSv1.0 certificates // See https://github.com/GSConnect/gnome-shell-extension-gsconnect/issues/930 imports.gi.GLib.setenv('G_TLS_GNUTLS_PRIORITY', 'NORMAL:%COMPAT:+VERS-TLS1.0', true); imports.gi.versions.Gdk = '3.0'; imports.gi.versions.GdkPixbuf = '2.0'; imports.gi.versions.Gio = '2.0'; imports.gi.versions.GIRepository = '2.0'; imports.gi.versions.GLib = '2.0'; imports.gi.versions.GObject = '2.0'; imports.gi.versions.Gtk = '3.0'; imports.gi.versions.Pango = '1.0'; const Gdk = imports.gi.Gdk; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; // Bootstrap function get_datadir() { const m = /@(.+):\d+/.exec((new Error()).stack.split('\n')[1]); return Gio.File.new_for_path(m[1]).get_parent().get_parent().get_path(); } imports.searchPath.unshift(get_datadir()); imports.config.PACKAGE_DATADIR = imports.searchPath[0]; // Local Imports const Config = imports.config; const Manager = imports.service.manager; const ServiceUI = imports.service.ui.service; /** * Class representing the GSConnect service daemon. */ const Service = GObject.registerClass({ GTypeName: 'GSConnectService', }, class Service extends Gtk.Application { _init() { super._init({ application_id: 'org.gnome.Shell.Extensions.GSConnect', flags: Gio.ApplicationFlags.HANDLES_OPEN, resource_base_path: '/org/gnome/Shell/Extensions/GSConnect', }); GLib.set_prgname('gsconnect'); GLib.set_application_name('GSConnect'); // Command-line this._initOptions(); } get settings() { if (this._settings === undefined) { this._settings = new Gio.Settings({ settings_schema: Config.GSCHEMA.lookup(Config.APP_ID, true), }); } return this._settings; } /* * GActions */ _initActions() { const actions = [ ['connect', this._identify.bind(this), new GLib.VariantType('s')], ['device', this._device.bind(this), new GLib.VariantType('(ssbv)')], ['error', this._error.bind(this), new GLib.VariantType('a{ss}')], ['preferences', this._preferences, null], ['quit', () => this.quit(), null], ['refresh', this._identify.bind(this), null], ]; for (const [name, callback, type] of actions) { const action = new Gio.SimpleAction({ name: name, parameter_type: type, }); action.connect('activate', callback); this.add_action(action); } } /** * A wrapper for Device GActions. This is used to route device notification * actions to their device, since GNotifications need an 'app' level action. * * @param {Gio.Action} action - The GAction * @param {GLib.Variant} parameter - The activation parameter */ _device(action, parameter) { try { parameter = parameter.unpack(); // Select the appropriate device(s) let devices; const id = parameter[0].unpack(); if (id === '*') devices = this.manager.devices.values(); else devices = [this.manager.devices.get(id)]; // Unpack the action data and activate the action const name = parameter[1].unpack(); const target = parameter[2].unpack() ? parameter[3].unpack() : null; for (const device of devices) device.activate_action(name, target); } catch (e) { logError(e); } } _error(action, parameter) { try { const error = parameter.deepUnpack(); // If there's a URL, we have better information in the Wiki if (error.url !== undefined) { Gio.AppInfo.launch_default_for_uri_async( error.url, null, null, null ); return; } const dialog = new ServiceUI.ErrorDialog(error); dialog.present(); } catch (e) { logError(e); } } _identify(action, parameter) { try { let uri = null; if (parameter instanceof GLib.Variant) uri = parameter.unpack(); this.manager.identify(uri); } catch (e) { logError(e); } } _preferences() { Gio.Subprocess.new( [`${Config.PACKAGE_DATADIR}/gsconnect-preferences`], Gio.SubprocessFlags.NONE ); } /** * Report a service-level error * * @param {Object} error - An Error or object with name, message and stack */ notify_error(error) { try { // Always log the error logError(error); // Create an new notification let id, body, priority; const notif = new Gio.Notification(); const icon = new Gio.ThemedIcon({name: 'dialog-error'}); let target = null; if (error.name === undefined) error.name = 'Error'; if (error.url !== undefined) { id = error.url; body = _('Click for help troubleshooting'); priority = Gio.NotificationPriority.URGENT; target = new GLib.Variant('a{ss}', { name: error.name.trim(), message: error.message.trim(), stack: error.stack.trim(), url: error.url, }); } else { id = error.message.trim(); body = _('Click for more information'); priority = Gio.NotificationPriority.HIGH; target = new GLib.Variant('a{ss}', { name: error.name.trim(), message: error.message.trim(), stack: error.stack.trim(), }); } notif.set_title(`GSConnect: ${error.name.trim()}`); notif.set_body(body); notif.set_icon(icon); notif.set_priority(priority); notif.set_default_action_and_target('app.error', target); this.send_notification(id, notif); } catch (e) { logError(e); } } vfunc_activate() { super.vfunc_activate(); } vfunc_startup() { super.vfunc_startup(); this.hold(); // Watch *this* file and stop the service if it's updated/uninstalled this._serviceMonitor = Gio.File.new_for_path( `${Config.PACKAGE_DATADIR}/service/daemon.js` ).monitor(Gio.FileMonitorFlags.WATCH_MOVES, null); this._serviceMonitor.connect('changed', () => this.quit()); // Init some resources const provider = new Gtk.CssProvider(); provider.load_from_resource(`${Config.APP_PATH}/application.css`); Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ); // Ensure our handlers are registered try { const appInfo = Gio.DesktopAppInfo.new(`${Config.APP_ID}.desktop`); appInfo.add_supports_type('x-scheme-handler/sms'); appInfo.add_supports_type('x-scheme-handler/tel'); } catch (e) { debug(e); } // GActions & GSettings this._initActions(); this.manager.start(); } vfunc_dbus_register(connection, object_path) { if (!super.vfunc_dbus_register(connection, object_path)) return false; this.manager = new Manager.Manager({ connection: connection, object_path: object_path, }); return true; } vfunc_dbus_unregister(connection, object_path) { this.manager.destroy(); super.vfunc_dbus_unregister(connection, object_path); } vfunc_open(files, hint) { super.vfunc_open(files, hint); for (const file of files) { let action, parameter, title; try { switch (file.get_uri_scheme()) { case 'sms': title = _('Send SMS'); action = 'uriSms'; parameter = new GLib.Variant('s', file.get_uri()); break; case 'tel': title = _('Dial Number'); action = 'shareUri'; parameter = new GLib.Variant('s', file.get_uri()); break; case 'file': title = _('Share File'); action = 'shareFile'; parameter = new GLib.Variant('(sb)', [file.get_uri(), false]); break; default: throw new Error(`Unsupported URI: ${file.get_uri()}`); } // Show chooser dialog new ServiceUI.DeviceChooser({ title: title, action_name: action, action_target: parameter, }); } catch (e) { logError(e, `GSConnect: Opening ${file.get_uri()}`); } } } vfunc_shutdown() { // Dispose GSettings if (this._settings !== undefined) this.settings.run_dispose(); this.manager.stop(); // Exhaust the event loop to ensure any pending operations complete const context = GLib.MainContext.default(); while (context.iteration(false)) continue; // Force a GC to prevent any more calls back into JS, then chain-up imports.system.gc(); super.vfunc_shutdown(); } /* * CLI */ _initOptions() { /* * Device Listings */ this.add_main_option( 'list-devices', 'l'.charCodeAt(0), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _('List available devices'), null ); this.add_main_option( 'list-all', 'a'.charCodeAt(0), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _('List all devices'), null ); this.add_main_option( 'device', 'd'.charCodeAt(0), GLib.OptionFlags.NONE, GLib.OptionArg.STRING, _('Target Device'), '' ); /** * Pairing */ this.add_main_option( 'pair', 0, GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _('Pair'), null ); this.add_main_option( 'unpair', 0, GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _('Unpair'), null ); /* * Messaging */ this.add_main_option( 'message', 0, GLib.OptionFlags.NONE, GLib.OptionArg.STRING_ARRAY, _('Send SMS'), '' ); this.add_main_option( 'message-body', 0, GLib.OptionFlags.NONE, GLib.OptionArg.STRING, _('Message Body'), '' ); /* * Notifications */ this.add_main_option( 'notification', 0, GLib.OptionFlags.NONE, GLib.OptionArg.STRING, _('Send Notification'), '' ); this.add_main_option( 'notification-appname', 0, GLib.OptionFlags.NONE, GLib.OptionArg.STRING, _('Notification App Name'), '<name>' ); this.add_main_option( 'notification-body', 0, GLib.OptionFlags.NONE, GLib.OptionArg.STRING, _('Notification Body'), '<text>' ); this.add_main_option( 'notification-icon', 0, GLib.OptionFlags.NONE, GLib.OptionArg.STRING, _('Notification Icon'), '<icon-name>' ); this.add_main_option( 'notification-id', 0, GLib.OptionFlags.NONE, GLib.OptionArg.STRING, _('Notification ID'), '<id>' ); this.add_main_option( 'photo', 0, GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _('Photo'), null ); this.add_main_option( 'ping', 0, GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _('Ping'), null ); this.add_main_option( 'ring', 0, GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _('Ring'), null ); /* * Sharing */ this.add_main_option( 'share-file', 0, GLib.OptionFlags.NONE, GLib.OptionArg.FILENAME_ARRAY, _('Share File'), '<filepath|URI>' ); this.add_main_option( 'share-link', 0, GLib.OptionFlags.NONE, GLib.OptionArg.STRING_ARRAY, _('Share Link'), '<URL>' ); this.add_main_option( 'share-text', 0, GLib.OptionFlags.NONE, GLib.OptionArg.STRING, _('Share Text'), '<text>' ); /* * Misc */ this.add_main_option( 'version', 'v'.charCodeAt(0), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _('Show release version'), null ); } _cliAction(id, name, parameter = null) { const parameters = []; if (parameter instanceof GLib.Variant) parameters[0] = parameter; id = id.replace(/\W+/g, '_'); Gio.DBus.session.call_sync( 'org.gnome.Shell.Extensions.GSConnect', `/org/gnome/Shell/Extensions/GSConnect/Device/${id}`, 'org.gtk.Actions', 'Activate', GLib.Variant.new('(sava{sv})', [name, parameters, {}]), null, Gio.DBusCallFlags.NONE, -1, null ); } _cliListDevices(full = true) { const result = Gio.DBus.session.call_sync( 'org.gnome.Shell.Extensions.GSConnect', '/org/gnome/Shell/Extensions/GSConnect', 'org.freedesktop.DBus.ObjectManager', 'GetManagedObjects', null, null, Gio.DBusCallFlags.NONE, -1, null ); const variant = result.unpack()[0].unpack(); let device; for (let object of Object.values(variant)) { object = object.recursiveUnpack(); device = object['org.gnome.Shell.Extensions.GSConnect.Device']; if (full) print(`${device.Id}\t${device.Name}\t${device.Connected}\t${device.Paired}`); else if (device.Connected && device.Paired) print(device.Id); } } _cliMessage(id, options) { if (!options.contains('message-body')) throw new TypeError('missing --message-body option'); // TODO: currently we only support single-recipient messaging const addresses = options.lookup_value('message', null).deepUnpack(); const body = options.lookup_value('message-body', null).deepUnpack(); this._cliAction( id, 'sendSms', GLib.Variant.new('(ss)', [addresses[0], body]) ); } _cliNotify(id, options) { const title = options.lookup_value('notification', null).unpack(); let body = ''; let icon = null; let nid = `${Date.now()}`; let appName = 'GSConnect CLI'; if (options.contains('notification-id')) nid = options.lookup_value('notification-id', null).unpack(); if (options.contains('notification-body')) body = options.lookup_value('notification-body', null).unpack(); if (options.contains('notification-appname')) appName = options.lookup_value('notification-appname', null).unpack(); if (options.contains('notification-icon')) { icon = options.lookup_value('notification-icon', null).unpack(); icon = Gio.Icon.new_for_string(icon); } else { icon = new Gio.ThemedIcon({ name: 'org.gnome.Shell.Extensions.GSConnect', }); } const notification = new GLib.Variant('a{sv}', { appName: GLib.Variant.new_string(appName), id: GLib.Variant.new_string(nid), title: GLib.Variant.new_string(title), text: GLib.Variant.new_string(body), ticker: GLib.Variant.new_string(`${title}: ${body}`), time: GLib.Variant.new_string(`${Date.now()}`), isClearable: GLib.Variant.new_boolean(true), icon: icon.serialize(), }); this._cliAction(id, 'sendNotification', notification); } _cliShareFile(device, options) { const files = options.lookup_value('share-file', null).deepUnpack(); for (let file of files) { file = imports.byteArray.toString(file); this._cliAction(device, 'shareFile', GLib.Variant.new('(sb)', [file, false])); } } _cliShareLink(device, options) { const uris = options.lookup_value('share-link', null).unpack(); for (const uri of uris) this._cliAction(device, 'shareUri', uri); } _cliShareText(device, options) { const text = options.lookup_value('share-text', null).unpack(); this._cliAction(device, 'shareText', GLib.Variant.new_string(text)); } vfunc_handle_local_options(options) { try { if (options.contains('version')) { print(`GSConnect ${Config.PACKAGE_VERSION}`); return 0; } this.register(null); if (options.contains('list-devices')) { this._cliListDevices(false); return 0; } if (options.contains('list-all')) { this._cliListDevices(true); return 0; } // We need a device for anything else; exit since this is probably // the daemon being started. if (!options.contains('device')) return -1; const id = options.lookup_value('device', null).unpack(); // Pairing if (options.contains('pair')) { this._cliAction(id, 'pair'); return 0; } if (options.contains('unpair')) { this._cliAction(id, 'unpair'); return 0; } // Plugins if (options.contains('message')) this._cliMessage(id, options); if (options.contains('notification')) this._cliNotify(id, options); if (options.contains('photo')) this._cliAction(id, 'photo'); if (options.contains('ping')) this._cliAction(id, 'ping', GLib.Variant.new_string('')); if (options.contains('ring')) this._cliAction(id, 'ring'); if (options.contains('share-file')) this._cliShareFile(id, options); if (options.contains('share-link')) this._cliShareLink(id, options); if (options.contains('share-text')) this._cliShareText(id, options); return 0; } catch (e) { logError(e); return 1; } } }); (new Service()).run([imports.system.programInvocationName].concat(ARGV)); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/device.js��������������������������������������������0000664�0000000�0000000�00000076030�14215434441�0023541�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Config = imports.config; const Components = imports.service.components; const Core = imports.service.core; /** * An object representing a remote device. * * Device class is subclassed from Gio.SimpleActionGroup so it implements the * GActionGroup and GActionMap interfaces, like Gio.Application. * */ var Device = GObject.registerClass({ GTypeName: 'GSConnectDevice', Properties: { 'connected': GObject.ParamSpec.boolean( 'connected', 'Connected', 'Whether the device is connected', GObject.ParamFlags.READABLE, false ), 'contacts': GObject.ParamSpec.object( 'contacts', 'Contacts', 'The contacts store for this device', GObject.ParamFlags.READABLE, GObject.Object ), 'encryption-info': GObject.ParamSpec.string( 'encryption-info', 'Encryption Info', 'A formatted string with the local and remote fingerprints', GObject.ParamFlags.READABLE, null ), 'icon-name': GObject.ParamSpec.string( 'icon-name', 'Icon Name', 'Icon name representing the device', GObject.ParamFlags.READABLE, null ), 'id': GObject.ParamSpec.string( 'id', 'Id', 'The device hostname or other network unique id', GObject.ParamFlags.READABLE, '' ), 'name': GObject.ParamSpec.string( 'name', 'Name', 'The device name', GObject.ParamFlags.READABLE, null ), 'paired': GObject.ParamSpec.boolean( 'paired', 'Paired', 'Whether the device is paired', GObject.ParamFlags.READABLE, false ), 'type': GObject.ParamSpec.string( 'type', 'Type', 'The device type', GObject.ParamFlags.READABLE, null ), }, }, class Device extends Gio.SimpleActionGroup { _init(identity) { super._init(); this._id = identity.body.deviceId; // GLib.Source timeout id's for pairing requests this._incomingPairRequest = 0; this._outgoingPairRequest = 0; // Maps of name->Plugin, packet->Plugin, uuid->Transfer this._plugins = new Map(); this._handlers = new Map(); this._procs = new Set(); this._transfers = new Map(); this._outputLock = false; this._outputQueue = []; // GSettings this.settings = new Gio.Settings({ settings_schema: Config.GSCHEMA.lookup( 'org.gnome.Shell.Extensions.GSConnect.Device', true ), path: `/org/gnome/shell/extensions/gsconnect/device/${this.id}/`, }); // Watch for changes to supported and disabled plugins this._disabledPluginsChangedId = this.settings.connect( 'changed::disabled-plugins', this._onAllowedPluginsChanged.bind(this) ); this._supportedPluginsChangedId = this.settings.connect( 'changed::supported-plugins', this._onAllowedPluginsChanged.bind(this) ); this._registerActions(); this.menu = new Gio.Menu(); // Parse identity if initialized with a proper packet, otherwise load if (identity.id !== undefined) this._handleIdentity(identity); else this._loadPlugins(); } get channel() { if (this._channel === undefined) this._channel = null; return this._channel; } get connected() { if (this._connected === undefined) this._connected = false; return this._connected; } get connection_type() { const lastConnection = this.settings.get_string('last-connection'); return lastConnection.split('://')[0]; } get contacts() { const contacts = this._plugins.get('contacts'); if (contacts && contacts.settings.get_boolean('contacts-source')) return contacts._store; if (this._contacts === undefined) this._contacts = Components.acquire('contacts'); return this._contacts; } // FIXME: backend should do this stuff get encryption_info() { let remoteFingerprint = _('Not available'); let localFingerprint = _('Not available'); // Bluetooth connections have no certificate so we use the host address if (this.connection_type === 'bluetooth') { // TRANSLATORS: Bluetooth address for remote device return _('Bluetooth device at %s').format('???'); // If the device is connected use the certificate from the connection } else if (this.connected) { remoteFingerprint = this.channel.peer_certificate.sha256(); // Otherwise pull it out of the settings } else if (this.paired) { remoteFingerprint = Gio.TlsCertificate.new_from_pem( this.settings.get_string('certificate-pem'), -1 ).sha256(); } // FIXME: another ugly reach-around let lanBackend; if (this.service !== null) lanBackend = this.service.manager.backends.get('lan'); if (lanBackend && lanBackend.certificate) localFingerprint = lanBackend.certificate.sha256(); // TRANSLATORS: Label for TLS Certificate fingerprint // // Example: // // Google Pixel Fingerprint: // 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 return _('%s Fingerprint:').format(this.name) + '\n' + remoteFingerprint + '\n\n' + _('%s Fingerprint:').format('GSConnect') + '\n' + localFingerprint; } get id() { return this._id; } get name() { return this.settings.get_string('name'); } get paired() { return this.settings.get_boolean('paired'); } get icon_name() { switch (this.type) { case 'laptop': return 'laptop-symbolic'; case 'phone': return 'smartphone-symbolic'; case 'tablet': return 'tablet-symbolic'; case 'tv': return 'tv-symbolic'; case 'desktop': default: return 'computer-symbolic'; } } get service() { if (this._service === undefined) this._service = Gio.Application.get_default(); return this._service; } get type() { return this.settings.get_string('type'); } _handleIdentity(packet) { this.freeze_notify(); // If we're connected, record the reconnect URI if (this.channel !== null) this.settings.set_string('last-connection', this.channel.address); // The type won't change, but it might not be properly set yet if (this.type !== packet.body.deviceType) { this.settings.set_string('type', packet.body.deviceType); this.notify('type'); this.notify('icon-name'); } // The name may change so we check and notify if so if (this.name !== packet.body.deviceName) { this.settings.set_string('name', packet.body.deviceName); this.notify('name'); } // Packets const incoming = packet.body.incomingCapabilities.sort(); const outgoing = packet.body.outgoingCapabilities.sort(); const inc = this.settings.get_strv('incoming-capabilities'); const out = this.settings.get_strv('outgoing-capabilities'); // Only write GSettings if something has changed if (incoming.join('') !== inc.join('') || outgoing.join('') !== out.join('')) { this.settings.set_strv('incoming-capabilities', incoming); this.settings.set_strv('outgoing-capabilities', outgoing); } // Determine supported plugins by matching incoming to outgoing types const supported = []; for (const name in imports.service.plugins) { // Exclude mousepad/presenter plugins in unsupported sessions if (!HAVE_REMOTEINPUT && ['mousepad', 'presenter'].includes(name)) continue; const meta = imports.service.plugins[name].Metadata; if (meta === undefined) continue; // If we can handle packets it sends or send packets it can handle if (meta.incomingCapabilities.some(t => outgoing.includes(t)) || meta.outgoingCapabilities.some(t => incoming.includes(t))) supported.push(name); } // Only write GSettings if something has changed const currentSupported = this.settings.get_strv('supported-plugins'); if (currentSupported.join('') !== supported.sort().join('')) this.settings.set_strv('supported-plugins', supported); this.thaw_notify(); } /** * Set the channel and start sending/receiving packets. If %null is passed * the device becomes disconnected. * * @param {Core.Channel} [channel] - The new channel */ setChannel(channel = null) { if (this.channel === channel) return; if (this.channel !== null) this.channel.close(); this._channel = channel; // If we've disconnected empty the queue, otherwise restart the read // loop and update the device metadata if (this.channel === null) { this._outputQueue.length = 0; } else { this._handleIdentity(this.channel.identity); this._readLoop(channel); } // The connected state didn't change if (this.connected === !!this.channel) return; // Notify and trigger plugins this._connected = !!this.channel; this.notify('connected'); this._triggerPlugins(); } async _readLoop(channel) { try { let packet = null; while ((packet = await this.channel.readPacket())) { debug(packet, this.name); this.handlePacket(packet); } } catch (e) { if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) debug(e, this.name); if (this.channel === channel) this.setChannel(null); } } _processExit(proc, result) { try { proc.wait_check_finish(result); } catch (e) { debug(e); } this.delete(proc); } /** * Launch a subprocess for the device. If the device becomes unpaired, it is * assumed the device is no longer trusted and all subprocesses will be * killed. * * @param {string[]} args - process arguments * @param {Gio.Cancellable} [cancellable] - optional cancellable * @return {Gio.Subprocess} The subprocess */ launchProcess(args, cancellable = null) { if (this._launcher === undefined) { const application = GLib.build_filenamev([ Config.PACKAGE_DATADIR, 'service', 'daemon.js', ]); this._launcher = new Gio.SubprocessLauncher(); this._launcher.setenv('GSCONNECT', application, false); this._launcher.setenv('GSCONNECT_DEVICE_ID', this.id, false); this._launcher.setenv('GSCONNECT_DEVICE_NAME', this.name, false); this._launcher.setenv('GSCONNECT_DEVICE_ICON', this.icon_name, false); this._launcher.setenv( 'GSCONNECT_DEVICE_DBUS', `${Config.APP_PATH}/Device/${this.id.replace(/\W+/g, '_')}`, false ); } // Create and track the process const proc = this._launcher.spawnv(args); proc.wait_check_async(cancellable, this._processExit.bind(this._procs)); this._procs.add(proc); return proc; } /** * Handle a packet and pass it to the appropriate plugin. * * @param {Core.Packet} packet - The incoming packet object * @return {undefined} no return value */ handlePacket(packet) { try { if (packet.type === 'kdeconnect.pair') return this._handlePair(packet); // The device must think we're paired; inform it we are not if (!this.paired) return this.unpair(); const handler = this._handlers.get(packet.type); if (handler !== undefined) handler.handlePacket(packet); else debug(`Unsupported packet type (${packet.type})`, this.name); } catch (e) { debug(e, this.name); } } /** * Send a packet to the device. * * @param {Object} packet - An object of packet data... */ async sendPacket(packet) { try { if (!this.connected) return; if (!this.paired && packet.type !== 'kdeconnect.pair') return; this._outputQueue.push(new Core.Packet(packet)); if (this._outputLock) return; this._outputLock = true; let next; while ((next = this._outputQueue.shift())) { await this.channel.sendPacket(next); debug(next, this.name); } this._outputLock = false; } catch (e) { if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) debug(e, this.name); this._outputLock = false; } } /** * Actions */ _registerActions() { // Pairing notification actions const acceptPair = new Gio.SimpleAction({name: 'pair'}); acceptPair.connect('activate', this.pair.bind(this)); this.add_action(acceptPair); const rejectPair = new Gio.SimpleAction({name: 'unpair'}); rejectPair.connect('activate', this.unpair.bind(this)); this.add_action(rejectPair); // Transfer notification actions const cancelTransfer = new Gio.SimpleAction({ name: 'cancelTransfer', parameter_type: new GLib.VariantType('s'), }); cancelTransfer.connect('activate', this.cancelTransfer.bind(this)); this.add_action(cancelTransfer); const openPath = new Gio.SimpleAction({ name: 'openPath', parameter_type: new GLib.VariantType('s'), }); openPath.connect('activate', this.openPath); this.add_action(openPath); // Preference helpers const clearCache = new Gio.SimpleAction({ name: 'clearCache', parameter_type: null, }); clearCache.connect('activate', this._clearCache.bind(this)); this.add_action(clearCache); } /** * Get the position of a GMenuItem with @actionName in the top level of the * device menu. * * @param {string} actionName - An action name with scope (eg. device.foo) * @return {number} An 0-based index or -1 if not found */ getMenuAction(actionName) { for (let i = 0, len = this.menu.get_n_items(); i < len; i++) { try { const val = this.menu.get_item_attribute_value(i, 'action', null); if (val.unpack() === actionName) return i; } catch (e) { continue; } } return -1; } /** * Add a GMenuItem to the top level of the device menu * * @param {Gio.MenuItem} menuItem - A GMenuItem * @param {number} [index] - The position to place the item * @return {number} The position the item was placed */ addMenuItem(menuItem, index = -1) { try { if (index > -1) { this.menu.insert_item(index, menuItem); return index; } this.menu.append_item(menuItem); return this.menu.get_n_items(); } catch (e) { debug(e, this.name); return -1; } } /** * Add a Device GAction to the top level of the device menu * * @param {Gio.Action} action - A GAction * @param {number} [index] - The position to place the item * @param {string} label - A label for the item * @param {string} icon_name - A themed icon name for the item * @return {number} The position the item was placed */ addMenuAction(action, index = -1, label, icon_name) { try { const item = new Gio.MenuItem(); if (label) item.set_label(label); if (icon_name) item.set_icon(new Gio.ThemedIcon({name: icon_name})); item.set_attribute_value( 'hidden-when', new GLib.Variant('s', 'action-disabled') ); item.set_detailed_action(`device.${action.name}`); return this.addMenuItem(item, index); } catch (e) { debug(e, this.name); return -1; } } /** * Remove a GAction from the top level of the device menu by action name * * @param {string} actionName - A GAction name, including scope * @return {number} The position the item was removed from or -1 */ removeMenuAction(actionName) { try { const index = this.getMenuAction(actionName); if (index > -1) this.menu.remove(index); return index; } catch (e) { debug(e, this.name); return -1; } } /** * Withdraw a device notification. * * @param {string} id - Id for the notification to withdraw */ hideNotification(id) { if (this.service === null) return; this.service.withdraw_notification(`${this.id}|${id}`); } /** * Show a device notification. * * @param {Object} params - A dictionary of notification parameters * @param {number} [params.id] - A UNIX epoch timestamp (ms) * @param {string} [params.title] - A title * @param {string} [params.body] - A body * @param {Gio.Icon} [params.icon] - An icon * @param {Gio.NotificationPriority} [params.priority] - The priority * @param {Array} [params.actions] - A dictionary of action parameters * @param {Array} [params.buttons] - An Array of buttons */ showNotification(params) { if (this.service === null) return; // KDE Connect on Android can sometimes give an undefined for params.body Object.keys(params) .forEach(key => params[key] === undefined && delete params[key]); params = Object.assign({ id: Date.now(), title: this.name, body: '', icon: new Gio.ThemedIcon({name: this.icon_name}), priority: Gio.NotificationPriority.NORMAL, action: null, buttons: [], }, params); const notif = new Gio.Notification(); notif.set_title(params.title); notif.set_body(params.body); notif.set_icon(params.icon); notif.set_priority(params.priority); // Default Action if (params.action) { const hasParameter = (params.action.parameter !== null); if (!hasParameter) params.action.parameter = new GLib.Variant('s', ''); notif.set_default_action_and_target( 'app.device', new GLib.Variant('(ssbv)', [ this.id, params.action.name, hasParameter, params.action.parameter, ]) ); } // Buttons for (const button of params.buttons) { const hasParameter = (button.parameter !== null); if (!hasParameter) button.parameter = new GLib.Variant('s', ''); notif.add_button_with_target( button.label, 'app.device', new GLib.Variant('(ssbv)', [ this.id, button.action, hasParameter, button.parameter, ]) ); } this.service.send_notification(`${this.id}|${params.id}`, notif); } /** * Cancel an ongoing file transfer. * * @param {Gio.Action} action - The GAction * @param {GLib.Variant} parameter - The activation parameter */ cancelTransfer(action, parameter) { try { const uuid = parameter.unpack(); const transfer = this._transfers.get(uuid); if (transfer === undefined) return; this._transfers.delete(uuid); transfer.cancel(); } catch (e) { logError(e, this.name); } } /** * Create a transfer object. * * @return {Core.Transfer} A new transfer */ createTransfer() { const transfer = new Core.Transfer({device: this}); // Track the transfer this._transfers.set(transfer.uuid, transfer); transfer.connect('notify::completed', (transfer) => { this._transfers.delete(transfer.uuid); }); return transfer; } /** * Reject the transfer payload described by @packet. * * @param {Core.Packet} packet - A packet * @return {Promise} A promise for the operation */ rejectTransfer(packet) { if (!packet || !packet.hasPayload()) return; return this.channel.rejectTransfer(packet); } openPath(action, parameter) { const path = parameter.unpack(); // Normalize paths to URIs, assuming local file const uri = path.includes('://') ? path : `file://${path}`; Gio.AppInfo.launch_default_for_uri_async(uri, null, null, null); } _clearCache(action, parameter) { for (const plugin of this._plugins.values()) { try { plugin.clearCache(); } catch (e) { debug(e, this.name); } } } /** * Pair request handler * * @param {Core.Packet} packet - A complete kdeconnect.pair packet */ _handlePair(packet) { // A pair has been requested/confirmed if (packet.body.pair) { // The device is accepting our request if (this._outgoingPairRequest) { this._setPaired(true); this._loadPlugins(); // The device thinks we're unpaired } else if (this.paired) { this._setPaired(true); this.sendPacket({ type: 'kdeconnect.pair', body: {pair: true}, }); this._triggerPlugins(); // The device is requesting pairing } else { this._notifyPairRequest(); } // Device is requesting unpairing/rejecting our request } else { this._setPaired(false); this._unloadPlugins(); } } /** * Notify the user of an incoming pair request and set a 30s timeout */ _notifyPairRequest() { // Reset any active request this._resetPairRequest(); this.showNotification({ id: 'pair-request', // TRANSLATORS: eg. Pair Request from Google Pixel title: _('Pair Request from %s').format(this.name), body: this.encryption_info, icon: new Gio.ThemedIcon({name: 'channel-insecure-symbolic'}), priority: Gio.NotificationPriority.URGENT, buttons: [ { action: 'unpair', label: _('Reject'), parameter: null, }, { action: 'pair', label: _('Accept'), parameter: null, }, ], }); // Start a 30s countdown this._incomingPairRequest = GLib.timeout_add_seconds( GLib.PRIORITY_DEFAULT, 30, this._setPaired.bind(this, false) ); } /** * Reset pair request timeouts and withdraw any notifications */ _resetPairRequest() { this.hideNotification('pair-request'); if (this._incomingPairRequest) { GLib.source_remove(this._incomingPairRequest); this._incomingPairRequest = 0; } if (this._outgoingPairRequest) { GLib.source_remove(this._outgoingPairRequest); this._outgoingPairRequest = 0; } } /** * Set the internal paired state of the device and emit ::notify * * @param {boolean} paired - The paired state to set */ _setPaired(paired) { this._resetPairRequest(); // For TCP connections we store or reset the TLS Certificate if (this.connection_type === 'lan') { if (paired) { this.settings.set_string( 'certificate-pem', this.channel.peer_certificate.certificate_pem ); } else { this.settings.reset('certificate-pem'); } } // If we've become unpaired, stop all subprocesses and transfers if (!paired) { for (const proc of this._procs) proc.force_exit(); this._procs.clear(); for (const transfer of this._transfers.values()) transfer.close(); this._transfers.clear(); } this.settings.set_boolean('paired', paired); this.notify('paired'); } /** * Send or accept an incoming pair request; also exported as a GAction */ pair() { try { // If we're accepting an incoming pair request, set the internal // paired state and send the confirmation before loading plugins. if (this._incomingPairRequest) { this._setPaired(true); this.sendPacket({ type: 'kdeconnect.pair', body: {pair: true}, }); this._loadPlugins(); // If we're initiating an outgoing pair request, be sure the timer // is reset before sending the request and setting a 30s timeout. } else if (!this.paired) { this._resetPairRequest(); this.sendPacket({ type: 'kdeconnect.pair', body: {pair: true}, }); this._outgoingPairRequest = GLib.timeout_add_seconds( GLib.PRIORITY_DEFAULT, 30, this._setPaired.bind(this, false) ); } } catch (e) { logError(e, this.name); } } /** * Unpair or reject an incoming pair request; also exported as a GAction */ unpair() { try { if (this.connected) { this.sendPacket({ type: 'kdeconnect.pair', body: {pair: false}, }); } this._setPaired(false); this._unloadPlugins(); } catch (e) { logError(e, this.name); } } /* * Plugin Functions */ _onAllowedPluginsChanged(settings) { const disabled = this.settings.get_strv('disabled-plugins'); const supported = this.settings.get_strv('supported-plugins'); const allowed = supported.filter(name => !disabled.includes(name)); // Unload any plugins that are disabled or unsupported this._plugins.forEach(plugin => { if (!allowed.includes(plugin.name)) this._unloadPlugin(plugin.name); }); // Make sure we change the contacts store if the plugin was disabled if (!allowed.includes('contacts')) this.notify('contacts'); // Load allowed plugins for (const name of allowed) this._loadPlugin(name); } _loadPlugin(name) { let handler, plugin; try { if (this.paired && !this._plugins.has(name)) { // Instantiate the handler handler = imports.service.plugins[name]; plugin = new handler.Plugin(this); // Register packet handlers for (const packetType of handler.Metadata.incomingCapabilities) this._handlers.set(packetType, plugin); // Register plugin this._plugins.set(name, plugin); // Run the connected()/disconnected() handler if (this.connected) plugin.connected(); else plugin.disconnected(); } } catch (e) { if (plugin !== undefined) plugin.destroy(); if (this.service !== null) this.service.notify_error(e); else logError(e, this.name); } } async _loadPlugins() { const disabled = this.settings.get_strv('disabled-plugins'); for (const name of this.settings.get_strv('supported-plugins')) { if (!disabled.includes(name)) await this._loadPlugin(name); } } _unloadPlugin(name) { let handler, plugin; try { if (this._plugins.has(name)) { // Unregister packet handlers handler = imports.service.plugins[name]; for (const type of handler.Metadata.incomingCapabilities) this._handlers.delete(type); // Unregister plugin plugin = this._plugins.get(name); this._plugins.delete(name); plugin.destroy(); } } catch (e) { logError(e, this.name); } } async _unloadPlugins() { for (const name of this._plugins.keys()) await this._unloadPlugin(name); } _triggerPlugins() { for (const plugin of this._plugins.values()) { if (this.connected) plugin.connected(); else plugin.disconnected(); } } destroy() { // Drop the default contacts store if we were using it if (this._contacts !== undefined) this._contacts = Components.release('contacts'); // Close the channel if still connected if (this.channel !== null) this.channel.close(); // Synchronously destroy plugins this._plugins.forEach(plugin => plugin.destroy()); this._plugins.clear(); // Dispose GSettings this.settings.disconnect(this._disabledPluginsChangedId); this.settings.disconnect(this._supportedPluginsChangedId); this.settings.run_dispose(); GObject.signal_handlers_destroy(this); } }); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/manager.js�������������������������������������������0000664�0000000�0000000�00000033077�14215434441�0023720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Config = imports.config; const DBus = imports.service.utils.dbus; const Device = imports.service.device; const DEVICE_NAME = 'org.gnome.Shell.Extensions.GSConnect.Device'; const DEVICE_PATH = '/org/gnome/Shell/Extensions/GSConnect/Device'; const DEVICE_IFACE = Config.DBUS.lookup_interface(DEVICE_NAME); /** * A manager for devices. */ var Manager = GObject.registerClass({ GTypeName: 'GSConnectManager', Properties: { 'active': GObject.ParamSpec.boolean( 'active', 'Active', 'Whether the manager is active', GObject.ParamFlags.READABLE, false ), 'discoverable': GObject.ParamSpec.boolean( 'discoverable', 'Discoverable', 'Whether the service responds to discovery requests', GObject.ParamFlags.READWRITE, false ), 'id': GObject.ParamSpec.string( 'id', 'Id', 'The hostname or other network unique id', GObject.ParamFlags.READWRITE, null ), 'name': GObject.ParamSpec.string( 'name', 'Name', 'The name announced to the network', GObject.ParamFlags.READWRITE, 'GSConnect' ), }, }, class Manager extends Gio.DBusObjectManagerServer { _init(params = {}) { super._init(params); this._exported = new WeakMap(); this._reconnectId = 0; this._settings = new Gio.Settings({ settings_schema: Config.GSCHEMA.lookup(Config.APP_ID, true), }); this._initSettings(); } get active() { if (this._active === undefined) this._active = false; return this._active; } get backends() { if (this._backends === undefined) this._backends = new Map(); return this._backends; } get devices() { if (this._devices === undefined) this._devices = new Map(); return this._devices; } get discoverable() { if (this._discoverable === undefined) this._discoverable = this.settings.get_boolean('discoverable'); return this._discoverable; } set discoverable(value) { if (this.discoverable === value) return; this._discoverable = value; this.notify('discoverable'); // FIXME: This whole thing just keeps getting uglier const application = Gio.Application.get_default(); if (application === null) return; if (this.discoverable) { Gio.Application.prototype.withdraw_notification.call( application, 'discovery-warning' ); } else { const notif = new Gio.Notification(); notif.set_title(_('Discovery Disabled')); notif.set_body(_('Discovery has been disabled due to the number of devices on this network.')); notif.set_icon(new Gio.ThemedIcon({name: 'dialog-warning'})); notif.set_priority(Gio.NotificationPriority.HIGH); notif.set_default_action('app.preferences'); Gio.Application.prototype.withdraw_notification.call( application, 'discovery-warning', notif ); } } get id() { if (this._id === undefined) this._id = this.settings.get_string('id'); return this._id; } set id(value) { if (this.id === value) return; this._id = value; this.notify('id'); } get name() { if (this._name === undefined) this._name = this.settings.get_string('name'); return this._name; } set name(value) { if (this.name === value) return; this._name = value; this.notify('name'); // Broadcast changes to the network for (const backend of this.backends.values()) { backend.name = this.name; backend.buildIdentity(); } this.identify(); } get settings() { if (this._settings === undefined) { this._settings = new Gio.Settings({ settings_schema: Config.GSCHEMA.lookup(Config.APP_ID, true), }); } return this._settings; } vfunc_notify(pspec) { if (pspec.name !== 'connection') return; if (this.connection !== null) this._exportDevices(); else this._unexportDevices(); } /* * GSettings */ _initSettings() { // Initialize the ID and name of the service if (this.settings.get_string('id').length === 0) this.settings.set_string('id', GLib.uuid_string_random()); if (this.settings.get_string('name').length === 0) this.settings.set_string('name', GLib.get_host_name()); // Bound Properties this.settings.bind('discoverable', this, 'discoverable', 0); this.settings.bind('id', this, 'id', 0); this.settings.bind('name', this, 'name', 0); } /* * Backends */ _onChannel(backend, channel) { try { let device = this.devices.get(channel.identity.body.deviceId); switch (true) { // Proceed if this is an existing device... case (device !== undefined): break; // Or the connection is allowed... case this.discoverable || channel.allowed: device = this._ensureDevice(channel.identity); break; // ...otherwise bail default: debug(`${channel.identity.body.deviceName}: not allowed`); return false; } device.setChannel(channel); return true; } catch (e) { logError(e, backend.name); return false; } } _loadBackends() { for (const name in imports.service.backends) { try { const module = imports.service.backends[name]; if (module.ChannelService === undefined) continue; // Try to create the backend and track it if successful const backend = new module.ChannelService({ id: this.id, name: this.name, }); this.backends.set(name, backend); // Connect to the backend backend.__channelId = backend.connect( 'channel', this._onChannel.bind(this) ); // Now try to start the backend, allowing us to retry if we fail backend.start(); } catch (e) { if (Gio.Application.get_default()) Gio.Application.get_default().notify_error(e); } } } /* * Devices */ _loadDevices() { // Load cached devices for (const id of this.settings.get_strv('devices')) { const device = new Device.Device({body: {deviceId: id}}); this._exportDevice(device); this.devices.set(id, device); } } _exportDevice(device) { if (this.connection === null) return; const info = { object: null, interface: null, actions: 0, menu: 0, }; const objectPath = `${DEVICE_PATH}/${device.id.replace(/\W+/g, '_')}`; // Export an object path for the device info.object = new Gio.DBusObjectSkeleton({ g_object_path: objectPath, }); this.export(info.object); // Export GActions & GMenu info.actions = Gio.DBus.session.export_action_group(objectPath, device); info.menu = Gio.DBus.session.export_menu_model(objectPath, device.menu); // Export the Device interface info.interface = new DBus.Interface({ g_instance: device, g_interface_info: DEVICE_IFACE, }); info.object.add_interface(info.interface); this._exported.set(device, info); } _exportDevices() { if (this.connection === null) return; for (const device of this.devices.values()) this._exportDevice(device); } _unexportDevice(device) { const info = this._exported.get(device); if (info === undefined) return; // Unexport GActions and GMenu Gio.DBus.session.unexport_action_group(info.actions); Gio.DBus.session.unexport_menu_model(info.menu); // Unexport the Device interface and object info.interface.flush(); info.object.remove_interface(info.interface); info.object.flush(); this.unexport(info.object.g_object_path); this._exported.delete(device); } _unexportDevices() { for (const device of this.devices.values()) this._unexportDevice(device); } /** * Return a device for @packet, creating it and adding it to the list of * of known devices if it doesn't exist. * * @param {Core.Packet} packet - An identity packet for the device * @return {Device.Device} A device object */ _ensureDevice(packet) { let device = this.devices.get(packet.body.deviceId); if (device === undefined) { debug(`Adding ${packet.body.deviceName}`); // TODO: Remove when all clients support bluetooth-like discovery // // If this is the third unpaired device to connect, we disable // discovery to avoid choking on networks with many devices const unpaired = Array.from(this.devices.values()).filter(dev => { return !dev.paired; }); if (unpaired.length === 3) this.discoverable = false; device = new Device.Device(packet); this._exportDevice(device); this.devices.set(device.id, device); // Notify this.settings.set_strv('devices', Array.from(this.devices.keys())); } return device; } /** * Permanently remove a device. * * Removes the device from the list of known devices, deletes all GSettings * and files. * * @param {string} id - The id of the device to delete */ _removeDevice(id) { // Delete all GSettings const settings_path = `/org/gnome/shell/extensions/gsconnect/${id}/`; GLib.spawn_command_line_async(`dconf reset -f ${settings_path}`); // Delete the cache const cache = GLib.build_filenamev([Config.CACHEDIR, id]); Gio.File.rm_rf(cache); // Forget the device this.devices.delete(id); this.settings.set_strv('devices', Array.from(this.devices.keys())); } /** * A GSourceFunc that tries to reconnect to each paired device, while * pruning unpaired devices that have disconnected. * * @return {boolean} Always %true */ _reconnect() { for (const [id, device] of this.devices) { if (device.connected) continue; if (device.paired) { this.identify(device.settings.get_string('last-connection')); continue; } this._unexportDevice(device); this._removeDevice(id); device.destroy(); } return GLib.SOURCE_CONTINUE; } /** * Identify to an address or broadcast to the network. * * @param {string} [uri] - An address URI or %null to broadcast */ identify(uri = null) { try { // If we're passed a parameter, try and find a backend for it if (uri !== null) { const [scheme, address] = uri.split('://'); const backend = this.backends.get(scheme); if (backend !== undefined) backend.broadcast(address); // If we're not discoverable, only try to reconnect known devices } else if (!this.discoverable) { this._reconnect(); // Otherwise have each backend broadcast to it's network } else { this.backends.forEach(backend => backend.broadcast()); } } catch (e) { logError(e); } } /** * Start managing devices. */ start() { if (this.active) return; this._loadDevices(); this._loadBackends(); if (this._reconnectId === 0) { this._reconnectId = GLib.timeout_add_seconds( GLib.PRIORITY_LOW, 5, this._reconnect.bind(this) ); } this._active = true; this.notify('active'); } /** * Stop managing devices. */ stop() { if (!this.active) return; if (this._reconnectId > 0) { GLib.Source.remove(this._reconnectId); this._reconnectId = 0; } this._unexportDevices(); this.backends.forEach(backend => backend.destroy()); this.backends.clear(); this.devices.forEach(device => device.destroy()); this.devices.clear(); this._active = false; this.notify('active'); } /** * Stop managing devices and free any resources. */ destroy() { this.stop(); this.set_connection(null); } }); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/nativeMessagingHost.js�������������������������������0000775�0000000�0000000�00000014142�14215434441�0026263�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env gjs 'use strict'; imports.gi.versions.Gio = '2.0'; imports.gi.versions.GLib = '2.0'; imports.gi.versions.GObject = '2.0'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const System = imports.system; const NativeMessagingHost = GObject.registerClass({ GTypeName: 'GSConnectNativeMessagingHost', }, class NativeMessagingHost extends Gio.Application { _init() { super._init({ application_id: 'org.gnome.Shell.Extensions.GSConnect.NativeMessagingHost', flags: Gio.ApplicationFlags.NON_UNIQUE, }); } get devices() { if (this._devices === undefined) this._devices = {}; return this._devices; } vfunc_activate() { super.vfunc_activate(); } vfunc_startup() { super.vfunc_startup(); this.hold(); // IO Channels this._stdin = new Gio.DataInputStream({ base_stream: new Gio.UnixInputStream({fd: 0}), byte_order: Gio.DataStreamByteOrder.HOST_ENDIAN, }); this._stdout = new Gio.DataOutputStream({ base_stream: new Gio.UnixOutputStream({fd: 1}), byte_order: Gio.DataStreamByteOrder.HOST_ENDIAN, }); const source = this._stdin.base_stream.create_source(null); source.set_callback(this.receive.bind(this)); source.attach(null); // Device Manager try { this._manager = Gio.DBusObjectManagerClient.new_for_bus_sync( Gio.BusType.SESSION, Gio.DBusObjectManagerClientFlags.DO_NOT_AUTO_START, 'org.gnome.Shell.Extensions.GSConnect', '/org/gnome/Shell/Extensions/GSConnect', null, null ); } catch (e) { logError(e); this.quit(); } // Add currently managed devices for (const object of this._manager.get_objects()) { for (const iface of object.get_interfaces()) this._onInterfaceAdded(this._manager, object, iface); } // Watch for new and removed devices this._manager.connect( 'interface-added', this._onInterfaceAdded.bind(this) ); this._manager.connect( 'object-removed', this._onObjectRemoved.bind(this) ); // Watch for device property changes this._manager.connect( 'interface-proxy-properties-changed', this.sendDeviceList.bind(this) ); // Watch for service restarts this._manager.connect( 'notify::name-owner', this.sendDeviceList.bind(this) ); this.send({ type: 'connected', data: (this._manager.name_owner !== null), }); } receive() { try { // Read the message const length = this._stdin.read_int32(null); const bytes = this._stdin.read_bytes(length, null).toArray(); const message = JSON.parse(imports.byteArray.toString(bytes)); // A request for a list of devices if (message.type === 'devices') { this.sendDeviceList(); // A request to invoke an action } else if (message.type === 'share') { let actionName; const device = this.devices[message.data.device]; if (device) { if (message.data.action === 'share') actionName = 'shareUri'; else if (message.data.action === 'telephony') actionName = 'shareSms'; device.actions.activate_action( actionName, new GLib.Variant('s', message.data.url) ); } } return GLib.SOURCE_CONTINUE; } catch (e) { this.quit(); } } send(message) { try { const data = JSON.stringify(message); this._stdout.put_int32(data.length, null); this._stdout.put_string(data, null); } catch (e) { this.quit(); } } sendDeviceList() { // Inform the WebExtension we're disconnected from the service if (this._manager && this._manager.name_owner === null) return this.send({type: 'connected', data: false}); // Collect all the devices with supported actions const available = []; for (const device of Object.values(this.devices)) { const share = device.actions.get_action_enabled('shareUri'); const telephony = device.actions.get_action_enabled('shareSms'); if (share || telephony) { available.push({ id: device.g_object_path, name: device.name, type: device.type, share: share, telephony: telephony, }); } } this.send({type: 'devices', data: available}); } _proxyGetter(name) { try { return this.get_cached_property(name).unpack(); } catch (e) { return null; } } _onInterfaceAdded(manager, object, iface) { Object.defineProperties(iface, { 'name': { get: this._proxyGetter.bind(iface, 'Name'), enumerable: true, }, // TODO: phase this out for icon-name 'type': { get: this._proxyGetter.bind(iface, 'Type'), enumerable: true, }, }); iface.actions = Gio.DBusActionGroup.get( iface.g_connection, iface.g_name, iface.g_object_path ); this.devices[iface.g_object_path] = iface; this.sendDeviceList(); } _onObjectRemoved(manager, object) { delete this.devices[object.g_object_path]; this.sendDeviceList(); } }); // NOTE: must not pass ARGV (new NativeMessagingHost()).run([System.programInvocationName]); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/plugin.js��������������������������������������������0000664�0000000�0000000�00000016343�14215434441�0023601�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const ByteArray = imports.byteArray; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Config = imports.config; /** * Base class for device plugins. */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectPlugin', Properties: { 'device': GObject.ParamSpec.object( 'device', 'Device', 'The device that owns this plugin', GObject.ParamFlags.READABLE, GObject.Object ), 'name': GObject.ParamSpec.string( 'name', 'Name', 'The device name', GObject.ParamFlags.READABLE, null ), }, }, class Plugin extends GObject.Object { _init(device, name, meta = null) { super._init(); this._device = device; this._name = name; this._meta = meta; if (this._meta === null) this._meta = imports.service.plugins[name].Metadata; // GSettings const schema = Config.GSCHEMA.lookup(this._meta.id, false); if (schema !== null) { this.settings = new Gio.Settings({ settings_schema: schema, path: `${device.settings.path}plugin/${name}/`, }); } // GActions this._gactions = []; if (this._meta.actions) { const menu = this.device.settings.get_strv('menu-actions'); for (const name in this._meta.actions) { const info = this._meta.actions[name]; this._registerAction(name, menu.indexOf(name), info); } } } get cancellable() { if (this._cancellable === undefined) this._cancellable = new Gio.Cancellable(); return this._cancellable; } get device() { return this._device; } get name() { return this._name; } _activateAction(action, parameter) { try { let args = null; if (parameter instanceof GLib.Variant) args = parameter.full_unpack(); if (Array.isArray(args)) this[action.name](...args); else this[action.name](args); } catch (e) { logError(e, action.name); } } _registerAction(name, menuIndex, info) { try { // Device Action const action = new Gio.SimpleAction({ name: name, parameter_type: info.parameter_type, enabled: false, }); action.connect('activate', this._activateAction.bind(this)); this.device.add_action(action); // Menu if (menuIndex > -1) { this.device.addMenuAction( action, menuIndex, info.label, info.icon_name ); } this._gactions.push(action); } catch (e) { logError(e, `${this.device.name}: ${this.name}`); } } /** * Called when the device connects. */ connected() { // Enabled based on device capabilities, which might change const incoming = this.device.settings.get_strv('incoming-capabilities'); const outgoing = this.device.settings.get_strv('outgoing-capabilities'); for (const action of this._gactions) { const info = this._meta.actions[action.name]; if (info.incoming.every(type => outgoing.includes(type)) && info.outgoing.every(type => incoming.includes(type))) action.set_enabled(true); } } /** * Called when the device disconnects. */ disconnected() { for (const action of this._gactions) action.set_enabled(false); } /** * Called when a packet is received that the plugin is a handler for. * * @param {Core.Packet} packet - A KDE Connect packet */ handlePacket(packet) { throw new GObject.NotImplementedError(); } /** * Cache JSON parseable properties on this object for persistence. The * filename ~/.cache/gsconnect/<device-id>/<plugin-name>.json will be used * to store the properties and values. * * Calling cacheProperties() opens a JSON cache file and reads any stored * properties and values onto the current instance. When destroy() * is called the properties are automatically stored in the same file. * * @param {Array} names - A list of this object's property names to cache */ async cacheProperties(names) { try { this._cacheProperties = names; // Ensure the device's cache directory exists const cachedir = GLib.build_filenamev([ Config.CACHEDIR, this.device.id, ]); GLib.mkdir_with_parents(cachedir, 448); this._cacheFile = Gio.File.new_for_path( GLib.build_filenamev([cachedir, `${this.name}.json`]) ); // Read the cache from disk await new Promise((resolve, reject) => { this._cacheFile.load_contents_async(null, (file, res) => { try { const contents = file.load_contents_finish(res)[1]; const cache = JSON.parse(ByteArray.toString(contents)); Object.assign(this, cache); resolve(); } catch (e) { reject(e); } }); }); } catch (e) { debug(e.message, `${this.device.name}: ${this.name}`); } finally { this.cacheLoaded(); } } /** * An overridable function that is invoked when the on-disk cache is being * cleared. Implementations should use this function to clear any in-memory * cache data. */ clearCache() {} /** * An overridable function that is invoked when the cache is done loading */ cacheLoaded() {} /** * Unregister plugin actions, write the cache (if applicable) and destroy * any dangling signal handlers. */ destroy() { // Cancel any pending plugin operations if (this._cancellable !== undefined) this._cancellable.cancel(); for (const action of this._gactions) { this.device.removeMenuAction(`device.${action.name}`); this.device.remove_action(action.name); } // Write the cache to disk synchronously if (this._cacheFile !== undefined) { try { // Build the cache const cache = {}; for (const name of this._cacheProperties) cache[name] = this[name]; this._cacheFile.replace_contents( JSON.stringify(cache, null, 2), null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, null ); } catch (e) { debug(e.message, `${this.device.name}: ${this.name}`); } } GObject.signal_handlers_destroy(this); } }); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/plugins/���������������������������������������������0000775�0000000�0000000�00000000000�14215434441�0023417�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/plugins/battery.js�����������������������������������0000664�0000000�0000000�00000027760�14215434441�0025443�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Components = imports.service.components; const PluginBase = imports.service.plugin; var Metadata = { label: _('Battery'), description: _('Exchange battery information'), id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.Battery', incomingCapabilities: [ 'kdeconnect.battery', 'kdeconnect.battery.request', ], outgoingCapabilities: [ 'kdeconnect.battery', 'kdeconnect.battery.request', ], actions: {}, }; /** * Battery Plugin * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/battery */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectBatteryPlugin', }, class Plugin extends PluginBase.Plugin { _init(device) { super._init(device, 'battery'); // Setup Cache; defaults are 90 minute charge, 1 day discharge this._chargeState = [54, 0, -1]; this._dischargeState = [864, 0, -1]; this._thresholdLevel = 25; this.cacheProperties([ '_chargeState', '_dischargeState', '_thresholdLevel', ]); // Export battery state as GAction this.__state = new Gio.SimpleAction({ name: 'battery', parameter_type: new GLib.VariantType('(bsii)'), state: this.state, }); this.device.add_action(this.__state); // Local Battery (UPower) this._upower = null; this._sendStatisticsId = this.settings.connect( 'changed::send-statistics', this._onSendStatisticsChanged.bind(this) ); this._onSendStatisticsChanged(this.settings); } get charging() { if (this._charging === undefined) this._charging = false; return this._charging; } get icon_name() { let icon; if (this.level === -1) return 'battery-missing-symbolic'; else if (this.level === 100) return 'battery-full-charged-symbolic'; else if (this.level < 3) icon = 'battery-empty'; else if (this.level < 10) icon = 'battery-caution'; else if (this.level < 30) icon = 'battery-low'; else if (this.level < 60) icon = 'battery-good'; else if (this.level >= 60) icon = 'battery-full'; if (this.charging) return `${icon}-charging-symbolic`; return `${icon}-symbolic`; } get level() { // This is what KDE Connect returns if the remote battery plugin is // disabled or still being loaded if (this._level === undefined) this._level = -1; return this._level; } get time() { if (this._time === undefined) this._time = 0; return this._time; } get state() { return new GLib.Variant( '(bsii)', [this.charging, this.icon_name, this.level, this.time] ); } cacheLoaded() { this._initEstimate(); this._sendState(); } clearCache() { this._chargeState = [54, 0, -1]; this._dischargeState = [864, 0, -1]; this._thresholdLevel = 25; this._initEstimate(); } connected() { super.connected(); this._requestState(); this._sendState(); } handlePacket(packet) { switch (packet.type) { case 'kdeconnect.battery': this._receiveState(packet); break; case 'kdeconnect.battery.request': this._sendState(); break; } } _onSendStatisticsChanged() { if (this.settings.get_boolean('send-statistics')) this._monitorState(); else this._unmonitorState(); } /** * Recalculate and update the estimated time remaining, but not the rate. */ _initEstimate() { let rate, level; // elision of [rate, time, level] if (this.charging) [rate,, level] = this._chargeState; else [rate,, level] = this._dischargeState; if (!Number.isFinite(rate) || rate < 1) rate = this.charging ? 864 : 90; if (!Number.isFinite(level) || level < 0) level = this.level; // Update the time remaining if (rate && this.charging) this._time = Math.floor(rate * (100 - level)); else if (rate && !this.charging) this._time = Math.floor(rate * level); this.__state.state = this.state; } /** * Recalculate the (dis)charge rate and update the estimated time remaining. */ _updateEstimate() { let rate, time, level; const newTime = Math.floor(Date.now() / 1000); const newLevel = this.level; // Load the state; ensure we have sane values for calculation if (this.charging) [rate, time, level] = this._chargeState; else [rate, time, level] = this._dischargeState; if (!Number.isFinite(rate) || rate < 1) rate = this.charging ? 54 : 864; if (!Number.isFinite(time) || time <= 0) time = newTime; if (!Number.isFinite(level) || level < 0) level = newLevel; // Update the rate; use a weighted average to account for missed changes // NOTE: (rate = seconds/percent) const ldiff = this.charging ? newLevel - level : level - newLevel; const tdiff = newTime - time; const newRate = tdiff / ldiff; if (newRate && Number.isFinite(newRate)) rate = Math.floor((rate * 0.4) + (newRate * 0.6)); // Store the state for the next recalculation if (this.charging) this._chargeState = [rate, newTime, newLevel]; else this._dischargeState = [rate, newTime, newLevel]; // Update the time remaining if (rate && this.charging) this._time = Math.floor(rate * (100 - newLevel)); else if (rate && !this.charging) this._time = Math.floor(rate * newLevel); this.__state.state = this.state; } /** * Notify the user the remote battery is full. */ _fullBatteryNotification() { if (!this.settings.get_boolean('full-battery-notification')) return; // Offer the option to ring the device, if available let buttons = []; if (this.device.get_action_enabled('ring')) { buttons = [{ label: _('Ring'), action: 'ring', parameter: null, }]; } this.device.showNotification({ id: 'battery|full', // TRANSLATORS: eg. Google Pixel: Battery is full title: _('%s: Battery is full').format(this.device.name), // TRANSLATORS: when the battery is fully charged body: _('Fully Charged'), icon: Gio.ThemedIcon.new('battery-full-charged-symbolic'), buttons: buttons, }); } /** * Notify the user the remote battery is at custom charge level. */ _customBatteryNotification() { if (!this.settings.get_boolean('custom-battery-notification')) return; // Offer the option to ring the device, if available let buttons = []; if (this.device.get_action_enabled('ring')) { buttons = [{ label: _('Ring'), action: 'ring', parameter: null, }]; } this.device.showNotification({ id: 'battery|custom', // TRANSLATORS: eg. Google Pixel: Battery has reached custom charge level title: _('%s: Battery has reached custom charge level').format(this.device.name), // TRANSLATORS: when the battery has reached custom charge level body: _('%d%% Charged').format(this.level), icon: Gio.ThemedIcon.new('battery-full-charged-symbolic'), buttons: buttons, }); } /** * Notify the user the remote battery is low. */ _lowBatteryNotification() { if (!this.settings.get_boolean('low-battery-notification')) return; // Offer the option to ring the device, if available let buttons = []; if (this.device.get_action_enabled('ring')) { buttons = [{ label: _('Ring'), action: 'ring', parameter: null, }]; } this.device.showNotification({ id: 'battery|low', // TRANSLATORS: eg. Google Pixel: Battery is low title: _('%s: Battery is low').format(this.device.name), // TRANSLATORS: eg. 15% remaining body: _('%d%% remaining').format(this.level), icon: Gio.ThemedIcon.new('battery-caution-symbolic'), buttons: buttons, }); } /** * Handle a remote battery update. * * @param {Core.Packet} packet - A kdeconnect.battery packet */ _receiveState(packet) { // Charging state changed this._charging = packet.body.isCharging; // Level changed if (this._level !== packet.body.currentCharge) { this._level = packet.body.currentCharge; // If the level is above the threshold hide the notification if (this._level > this._thresholdLevel) this.device.hideNotification('battery|low'); // The level just changed to/from custom level while charging if ((this._level === this.settings.get_uint('custom-battery-notification-value')) && this._charging) this._customBatteryNotification(); else this.device.hideNotification('battery|custom'); // The level just changed to/from full if (this._level === 100) this._fullBatteryNotification(); else this.device.hideNotification('battery|full'); } // Device considers the level low if (packet.body.thresholdEvent > 0) { this._lowBatteryNotification(); this._thresholdLevel = this.level; } this._updateEstimate(); } /** * Request the remote battery's current state */ _requestState() { this.device.sendPacket({ type: 'kdeconnect.battery.request', body: {request: true}, }); } /** * Report the local battery's current state */ _sendState() { if (this._upower === null || !this._upower.is_present) return; this.device.sendPacket({ type: 'kdeconnect.battery', body: { currentCharge: this._upower.level, isCharging: this._upower.charging, thresholdEvent: this._upower.threshold, }, }); } /* * UPower monitoring methods */ _monitorState() { try { // Currently only true if the remote device is a desktop (rare) const incoming = this.device.settings.get_strv('incoming-capabilities'); if (!incoming.includes('kdeconnect.battery')) return; this._upower = Components.acquire('upower'); this._upowerId = this._upower.connect( 'changed', this._sendState.bind(this) ); this._sendState(); } catch (e) { logError(e, this.device.name); this._unmonitorState(); } } _unmonitorState() { try { if (this._upower === null) return; this._upower.disconnect(this._upowerId); this._upower = Components.release('upower'); } catch (e) { logError(e, this.device.name); } } destroy() { this.device.remove_action('battery'); this.settings.disconnect(this._sendStatisticsId); this._unmonitorState(); super.destroy(); } }); ����������������gnome-shell-extension-gsconnect-50/src/service/plugins/clipboard.js���������������������������������0000664�0000000�0000000�00000011622�14215434441�0025716�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const GObject = imports.gi.GObject; const Components = imports.service.components; const PluginBase = imports.service.plugin; var Metadata = { label: _('Clipboard'), description: _('Share the clipboard content'), id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.Clipboard', incomingCapabilities: [ 'kdeconnect.clipboard', 'kdeconnect.clipboard.connect', ], outgoingCapabilities: [ 'kdeconnect.clipboard', 'kdeconnect.clipboard.connect', ], actions: { clipboardPush: { label: _('Clipboard Push'), icon_name: 'edit-paste-symbolic', parameter_type: null, incoming: [], outgoing: ['kdeconnect.clipboard'], }, clipboardPull: { label: _('Clipboard Pull'), icon_name: 'edit-copy-symbolic', parameter_type: null, incoming: ['kdeconnect.clipboard'], outgoing: [], }, }, }; /** * Clipboard Plugin * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/clipboard */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectClipboardPlugin', }, class Plugin extends PluginBase.Plugin { _init(device) { super._init(device, 'clipboard'); this._clipboard = Components.acquire('clipboard'); // Watch local clipboard for changes this._textChangedId = this._clipboard.connect( 'notify::text', this._onLocalClipboardChanged.bind(this) ); // Buffer content to allow selective sync this._localBuffer = this._clipboard.text; this._localTimestamp = 0; this._remoteBuffer = null; } connected() { super.connected(); // TODO: if we're not auto-syncing local->remote, but we are doing the // reverse, it's possible older remote content will end up // overwriting newer local content. if (!this.settings.get_boolean('send-content')) return; if (this._localBuffer === null && this._localTimestamp === 0) return; this.device.sendPacket({ type: 'kdeconnect.clipboard.connect', body: { content: this._localBuffer, timestamp: this._localTimestamp, }, }); } handlePacket(packet) { if (!packet.body.hasOwnProperty('content')) return; switch (packet.type) { case 'kdeconnect.clipboard': this._handleContent(packet); break; case 'kdeconnect.clipboard.connect': this._handleConnectContent(packet); break; } } _handleContent(packet) { this._onRemoteClipboardChanged(packet.body.content); } _handleConnectContent(packet) { if (packet.body.hasOwnProperty('timestamp') && packet.body.timestamp > this._localTimestamp) this._onRemoteClipboardChanged(packet.body.content); } /* * Store the local clipboard content and forward it if enabled */ _onLocalClipboardChanged(clipboard, pspec) { this._localBuffer = clipboard.text; this._localTimestamp = Date.now(); if (this.settings.get_boolean('send-content')) this.clipboardPush(); } /* * Store the remote clipboard content and apply it if enabled */ _onRemoteClipboardChanged(text) { this._remoteBuffer = text; if (this.settings.get_boolean('receive-content')) this.clipboardPull(); } /** * Copy to the remote clipboard; called by _onLocalClipboardChanged() */ clipboardPush() { // Don't sync if the clipboard is empty or not text if (this._localTimestamp === 0) return; if (this._remoteBuffer !== this._localBuffer) { this._remoteBuffer = this._localBuffer; // If the buffer is %null, the clipboard contains non-text content, // so we neither clear the remote clipboard nor pass the content if (this._localBuffer !== null) { this.device.sendPacket({ type: 'kdeconnect.clipboard', body: { content: this._localBuffer, }, }); } } } /** * Copy from the remote clipboard; called by _onRemoteClipboardChanged() */ clipboardPull() { if (this._localBuffer !== this._remoteBuffer) { this._localBuffer = this._remoteBuffer; this._localTimestamp = Date.now(); this._clipboard.text = this._remoteBuffer; } } destroy() { if (this._clipboard && this._textChangedId) { this._clipboard.disconnect(this._textChangedId); this._clipboard = Components.release('clipboard'); } super.destroy(); } }); ��������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/plugins/connectivity_report.js�����������������������0000664�0000000�0000000�00000011671�14215434441�0030074�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Components = imports.service.components; const PluginBase = imports.service.plugin; var Metadata = { label: _('Connectivity Report'), description: _('Display connectivity status'), id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.ConnectivityReport', incomingCapabilities: [ 'kdeconnect.connectivity_report', ], outgoingCapabilities: [ 'kdeconnect.connectivity_report.request', ], actions: {}, }; /** * Connectivity Report Plugin * https://invent.kde.org/network/kdeconnect-kde/-/tree/master/plugins/connectivity_report */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectConnectivityReportPlugin', }, class Plugin extends PluginBase.Plugin { _init(device) { super._init(device, 'connectivity_report'); // Export connectivity state as GAction this.__state = new Gio.SimpleAction({ name: 'connectivityReport', // ( // cellular_network_type, // cellular_network_type_icon, // cellular_network_strength(0..4), // cellular_network_strength_icon, // ) parameter_type: new GLib.VariantType('(ssis)'), state: this.state, }); this.device.add_action(this.__state); } get signal_strength() { if (this._signalStrength === undefined) this._signalStrength = -1; return this._signalStrength; } get network_type() { if (this._networkType === undefined) this._networkType = ''; return this._networkType; } get signal_strength_icon_name() { if (this.signal_strength === 0) return 'network-cellular-signal-none-symbolic'; // SIGNAL_STRENGTH_NONE_OR_UNKNOWN else if (this.signal_strength === 1) return 'network-cellular-signal-weak-symbolic'; // SIGNAL_STRENGTH_POOR else if (this.signal_strength === 2) return 'network-cellular-signal-ok-symbolic'; // SIGNAL_STRENGTH_MODERATE else if (this.signal_strength === 3) return 'network-cellular-signal-good-symbolic'; // SIGNAL_STRENGTH_GOOD else if (this.signal_strength >= 4) return 'network-cellular-signal-excellent-symbolic'; // SIGNAL_STRENGTH_GREAT return 'network-cellular-offline-symbolic'; // OFF (signal_strength == -1) } get network_type_icon_name() { if (this.network_type === 'GSM' || this.network_type === 'CDMA' || this.network_type === 'iDEN') return 'network-cellular-2g-symbolic'; else if (this.network_type === 'UMTS' || this.network_type === 'CDMA2000') return 'network-cellular-3g-symbolic'; else if (this.network_type === 'LTE') return 'network-cellular-4g-symbolic'; else if (this.network_type === 'EDGE') return 'network-cellular-edge-symbolic'; else if (this.network_type === 'GPRS') return 'network-cellular-gprs-symbolic'; else if (this.network_type === 'HSPA') return 'network-cellular-hspa-symbolic'; // FIXME: No icon for this! // https://gitlab.gnome.org/GNOME/adwaita-icon-theme/-/issues/114 else if (this.network_type === '5G') return 'network-cellular-symbolic'; return 'network-cellular-symbolic'; } get state() { return new GLib.Variant( '(ssis)', [ this.network_type, this.network_type_icon_name, this.signal_strength, this.signal_strength_icon_name, ] ); } connected() { super.connected(); this._requestState(); } handlePacket(packet) { switch (packet.type) { case 'kdeconnect.connectivity_report': this._receiveState(packet); break; } } /** * Handle a remote state update. * * @param {Core.Packet} packet - A kdeconnect.connectivity_report packet */ _receiveState(packet) { if (packet.body.signalStrengths) { // TODO: Only first SIM (subscriptionID) is supported at the moment const subs = Object.keys(packet.body.signalStrengths); const firstSub = Math.min.apply(null, subs); const data = packet.body.signalStrengths[firstSub]; this._networkType = data.networkType; this._signalStrength = data.signalStrength; } // Update DBus state this.__state.state = this.state; } /** * Request the remote device's connectivity state */ _requestState() { this.device.sendPacket({ type: 'kdeconnect.connectivity_report.request', body: {}, }); } destroy() { this.device.remove_action('connectivity_report'); super.destroy(); } }); �����������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/plugins/contacts.js����������������������������������0000664�0000000�0000000�00000032633�14215434441�0025602�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const PluginBase = imports.service.plugin; const Contacts = imports.service.components.contacts; /* * We prefer libebook's vCard parser if it's available */ var EBookContacts; try { EBookContacts = imports.gi.EBookContacts; } catch (e) { EBookContacts = null; } var Metadata = { label: _('Contacts'), description: _('Access contacts of the paired device'), id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.Contacts', incomingCapabilities: [ 'kdeconnect.contacts.response_uids_timestamps', 'kdeconnect.contacts.response_vcards', ], outgoingCapabilities: [ 'kdeconnect.contacts.request_all_uids_timestamps', 'kdeconnect.contacts.request_vcards_by_uid', ], actions: {}, }; /* * vCard 2.1 Patterns */ const VCARD_FOLDING = /\r\n |\r |\n |=\n/g; const VCARD_SUPPORTED = /^fn|tel|photo|x-kdeconnect/i; const VCARD_BASIC = /^([^:;]+):(.+)$/; const VCARD_TYPED = /^([^:;]+);([^:]+):(.+)$/; const VCARD_TYPED_KEY = /item\d{1,2}\./; const VCARD_TYPED_META = /([a-z]+)=(.*)/i; /** * Contacts Plugin * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/contacts */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectContactsPlugin', }, class Plugin extends PluginBase.Plugin { _init(device) { super._init(device, 'contacts'); this._store = new Contacts.Store(device.id); this._store.fetch = this._requestUids.bind(this); // Notify when the store is ready this._contactsStoreReadyId = this._store.connect( 'notify::context', () => this.device.notify('contacts') ); // Notify if the contacts source changes this._contactsSourceChangedId = this.settings.connect( 'changed::contacts-source', () => this.device.notify('contacts') ); // Load the cache this._store.load(); } clearCache() { this._store.clear(); } connected() { super.connected(); this._requestUids(); } handlePacket(packet) { switch (packet.type) { case 'kdeconnect.contacts.response_uids_timestamps': this._handleUids(packet); break; case 'kdeconnect.contacts.response_vcards': this._handleVCards(packet); break; } } _handleUids(packet) { try { const contacts = this._store.contacts; const remote_uids = packet.body.uids; let removed = false; delete packet.body.uids; // Usually a failed request, so avoid wiping the cache if (remote_uids.length === 0) return; // Delete any contacts that were removed on the device for (let i = 0, len = contacts.length; i < len; i++) { const contact = contacts[i]; if (!remote_uids.includes(contact.id)) { this._store.remove(contact.id, false); removed = true; } } // Build a list of new or updated contacts const uids = []; for (const [uid, timestamp] of Object.entries(packet.body)) { const contact = this._store.get_contact(uid); if (!contact || contact.timestamp !== timestamp) uids.push(uid); } // Send a request for any new or updated contacts if (uids.length) this._requestVCards(uids); // If we removed any contacts, save the cache if (removed) this._store.save(); } catch (e) { logError(e); } } /** * Decode a string encoded as "QUOTED-PRINTABLE" and return a regular string * * See: https://github.com/mathiasbynens/quoted-printable/blob/master/src/quoted-printable.js * * @param {string} input - The QUOTED-PRINTABLE string * @return {string} The decoded string */ _decodeQuotedPrintable(input) { return input // https://tools.ietf.org/html/rfc2045#section-6.7, rule 3 .replace(/[\t\x20]$/gm, '') // Remove hard line breaks preceded by `=` .replace(/=(?:\r\n?|\n|$)/g, '') // https://tools.ietf.org/html/rfc2045#section-6.7, note 1. .replace(/=([a-fA-F0-9]{2})/g, ($0, $1) => { const codePoint = parseInt($1, 16); return String.fromCharCode(codePoint); }); } /** * Decode a string encoded as "UTF-8" and return a regular string * * See: https://github.com/kvz/locutus/blob/master/src/php/xml/utf8_decode.js * * @param {string} input - The UTF-8 string * @return {string} The decoded string */ _decodeUTF8(input) { try { const output = []; let i = 0; let c1 = 0; let seqlen = 0; while (i < input.length) { c1 = input.charCodeAt(i) & 0xFF; seqlen = 0; if (c1 <= 0xBF) { c1 &= 0x7F; seqlen = 1; } else if (c1 <= 0xDF) { c1 &= 0x1F; seqlen = 2; } else if (c1 <= 0xEF) { c1 &= 0x0F; seqlen = 3; } else { c1 &= 0x07; seqlen = 4; } for (let ai = 1; ai < seqlen; ++ai) c1 = ((c1 << 0x06) | (input.charCodeAt(ai + i) & 0x3F)); if (seqlen === 4) { c1 -= 0x10000; output.push(String.fromCharCode(0xD800 | ((c1 >> 10) & 0x3FF))); output.push(String.fromCharCode(0xDC00 | (c1 & 0x3FF))); } else { output.push(String.fromCharCode(c1)); } i += seqlen; } return output.join(''); // Fallback to old unfaithful } catch (e) { try { return decodeURIComponent(escape(input)); // Say "chowdah" frenchie! } catch (e) { debug(e, `Failed to decode UTF-8 VCard field ${input}`); return input; } } } /** * Parse a vCard (v2.1 only) and return a dictionary of the fields * * See: http://jsfiddle.net/ARTsinn/P2t2P/ * * @param {string} vcard_data - The raw VCard data * @return {Object} dictionary of vCard data */ _parseVCard21(vcard_data) { // vcard skeleton const vcard = { fn: _('Unknown Contact'), tel: [], }; // Remove line folding and split const unfolded = vcard_data.replace(VCARD_FOLDING, ''); const lines = unfolded.split(/\r\n|\r|\n/); for (let i = 0, len = lines.length; i < len; i++) { const line = lines[i]; let results, key, type, value; // Empty line or a property we aren't interested in if (!line || !line.match(VCARD_SUPPORTED)) continue; // Basic Fields (fn, x-kdeconnect-timestamp, etc) if ((results = line.match(VCARD_BASIC))) { [, key, value] = results; vcard[key.toLowerCase()] = value; continue; } // Typed Fields (tel, adr, etc) if ((results = line.match(VCARD_TYPED))) { [, key, type, value] = results; key = key.replace(VCARD_TYPED_KEY, '').toLowerCase(); value = value.split(';'); type = type.split(';'); // Type(s) const meta = {}; for (let i = 0, len = type.length; i < len; i++) { const res = type[i].match(VCARD_TYPED_META); if (res) meta[res[1]] = res[2]; else meta[`type${i === 0 ? '' : i}`] = type[i].toLowerCase(); } // Value(s) if (vcard[key] === undefined) vcard[key] = []; // Decode QUOTABLE-PRINTABLE if (meta.ENCODING && meta.ENCODING === 'QUOTED-PRINTABLE') { delete meta.ENCODING; value = value.map(v => this._decodeQuotedPrintable(v)); } // Decode UTF-8 if (meta.CHARSET && meta.CHARSET === 'UTF-8') { delete meta.CHARSET; value = value.map(v => this._decodeUTF8(v)); } // Special case for FN (full name) if (key === 'fn') vcard[key] = value[0]; else vcard[key].push({meta: meta, value: value}); } } return vcard; } /** * Parse a vCard (v2.1 only) using native JavaScript and add it to the * contact store. * * @param {string} uid - The contact UID * @param {string} vcard_data - The raw vCard data */ async _parseVCardNative(uid, vcard_data) { try { const vcard = this._parseVCard21(vcard_data); const contact = { id: uid, name: vcard.fn, numbers: [], origin: 'device', timestamp: parseInt(vcard['x-kdeconnect-timestamp']), }; // Phone Numbers contact.numbers = vcard.tel.map(entry => { let type = 'unknown'; if (entry.meta && entry.meta.type) type = entry.meta.type; return {type: type, value: entry.value[0]}; }); // Avatar if (vcard.photo) { const data = GLib.base64_decode(vcard.photo[0].value[0]); contact.avatar = await this._store.storeAvatar(data); } this._store.add(contact); } catch (e) { debug(e, `Failed to parse VCard contact ${uid}`); } } /** * Parse a vCard using libebook and add it to the contact store. * * @param {string} uid - The contact UID * @param {string} vcard_data - The raw vCard data */ async _parseVCard(uid, vcard_data) { try { const contact = { id: uid, name: _('Unknown Contact'), numbers: [], origin: 'device', timestamp: 0, }; const evcard = EBookContacts.VCard.new_from_string(vcard_data); const attrs = evcard.get_attributes(); for (let i = 0, len = attrs.length; i < len; i++) { const attr = attrs[i]; let data, number; switch (attr.get_name().toLowerCase()) { case 'fn': contact.name = attr.get_value(); break; case 'tel': number = {value: attr.get_value(), type: 'unknown'}; if (attr.has_type('CELL')) number.type = 'cell'; else if (attr.has_type('HOME')) number.type = 'home'; else if (attr.has_type('WORK')) number.type = 'work'; contact.numbers.push(number); break; case 'x-kdeconnect-timestamp': contact.timestamp = parseInt(attr.get_value()); break; case 'photo': data = GLib.base64_decode(attr.get_value()); contact.avatar = await this._store.storeAvatar(data); break; } } this._store.add(contact); } catch (e) { debug(e, `Failed to parse VCard contact ${uid}`); } } /** * Handle an incoming list of contact vCards and pass them to the best * available parser. * * @param {Core.Packet} packet - A `kdeconnect.contacts.response_vcards` */ _handleVCards(packet) { try { // We don't use this delete packet.body.uids; // Parse each vCard and add the contact for (const [uid, vcard] of Object.entries(packet.body)) { if (EBookContacts) this._parseVCard(uid, vcard); else this._parseVCardNative(uid, vcard); } } catch (e) { logError(e, this.device.name); } } /** * Request a list of contact UIDs with timestamps. */ _requestUids() { this.device.sendPacket({ type: 'kdeconnect.contacts.request_all_uids_timestamps', }); } /** * Request the vCards for @uids. * * @param {string[]} uids - A list of contact UIDs */ _requestVCards(uids) { this.device.sendPacket({ type: 'kdeconnect.contacts.request_vcards_by_uid', body: { uids: uids, }, }); } destroy() { this._store.disconnect(this._contactsStoreReadyId); this.settings.disconnect(this._contactsSourceChangedId); super.destroy(); } }); �����������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/plugins/findmyphone.js�������������������������������0000664�0000000�0000000�00000014244�14215434441�0026302�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gdk = imports.gi.Gdk; const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Components = imports.service.components; const PluginBase = imports.service.plugin; var Metadata = { label: _('Find My Phone'), description: _('Ring your paired device'), id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.FindMyPhone', incomingCapabilities: ['kdeconnect.findmyphone.request'], outgoingCapabilities: ['kdeconnect.findmyphone.request'], actions: { ring: { label: _('Ring'), icon_name: 'phonelink-ring-symbolic', parameter_type: null, incoming: [], outgoing: ['kdeconnect.findmyphone.request'], }, }, }; /** * FindMyPhone Plugin * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/findmyphone */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectFindMyPhonePlugin', }, class Plugin extends PluginBase.Plugin { _init(device) { super._init(device, 'findmyphone'); this._dialog = null; this._player = Components.acquire('sound'); this._mixer = Components.acquire('pulseaudio'); } handlePacket(packet) { switch (packet.type) { case 'kdeconnect.findmyphone.request': this._handleRequest(); break; } } /** * Handle an incoming location request. */ _handleRequest() { try { // If this is a second request, stop announcing and return if (this._dialog !== null) { this._dialog.response(Gtk.ResponseType.DELETE_EVENT); return; } this._dialog = new Dialog({ device: this.device, plugin: this, }); this._dialog.connect('response', () => { this._dialog = null; }); } catch (e) { this._cancelRequest(); logError(e, this.device.name); } } /** * Cancel any ongoing ringing and destroy the dialog. */ _cancelRequest() { if (this._dialog !== null) this._dialog.response(Gtk.ResponseType.DELETE_EVENT); } /** * Request that the remote device announce it's location */ ring() { this.device.sendPacket({ type: 'kdeconnect.findmyphone.request', body: {}, }); } destroy() { this._cancelRequest(); if (this._mixer !== undefined) this._mixer = Components.release('pulseaudio'); if (this._player !== undefined) this._player = Components.release('sound'); super.destroy(); } }); /* * Used to ensure 'audible-bell' is enabled for fallback */ const _WM_SETTINGS = new Gio.Settings({ schema_id: 'org.gnome.desktop.wm.preferences', path: '/org/gnome/desktop/wm/preferences/', }); /** * A custom GtkMessageDialog for alerting of incoming requests */ const Dialog = GObject.registerClass({ GTypeName: 'GSConnectFindMyPhoneDialog', Properties: { 'device': GObject.ParamSpec.object( 'device', 'Device', 'The device associated with this window', GObject.ParamFlags.READWRITE, GObject.Object ), 'plugin': GObject.ParamSpec.object( 'plugin', 'Plugin', 'The plugin providing messages', GObject.ParamFlags.READWRITE, GObject.Object ), }, }, class Dialog extends Gtk.MessageDialog { _init(params) { super._init({ buttons: Gtk.ButtonsType.CLOSE, device: params.device, image: new Gtk.Image({ icon_name: 'phonelink-ring-symbolic', pixel_size: 512, halign: Gtk.Align.CENTER, hexpand: true, valign: Gtk.Align.CENTER, vexpand: true, visible: true, }), plugin: params.plugin, urgency_hint: true, }); this.set_keep_above(true); this.maximize(); this.message_area.destroy(); // If an output stream is available start fading the volume up if (this.plugin._mixer && this.plugin._mixer.output) { this._stream = this.plugin._mixer.output; this._previousMuted = this._stream.muted; this._previousVolume = this._stream.volume; this._stream.muted = false; this._stream.fade(0.85, 15); // Otherwise ensure audible-bell is enabled } else { this._previousBell = _WM_SETTINGS.get_boolean('audible-bell'); _WM_SETTINGS.set_boolean('audible-bell', true); } // Start the alarm if (this.plugin._player !== undefined) this.plugin._player.loopSound('phone-incoming-call', this.cancellable); // Show the dialog this.show_all(); } vfunc_key_press_event(event) { this.response(Gtk.ResponseType.DELETE_EVENT); return Gdk.EVENT_STOP; } vfunc_motion_notify_event(event) { this.response(Gtk.ResponseType.DELETE_EVENT); return Gdk.EVENT_STOP; } vfunc_response(response_id) { // Stop the alarm this.cancellable.cancel(); // Restore the mixer level if (this._stream) { this._stream.muted = this._previousMuted; this._stream.fade(this._previousVolume); // Restore the audible-bell } else { _WM_SETTINGS.set_boolean('audible-bell', this._previousBell); } this.destroy(); } get cancellable() { if (this._cancellable === undefined) this._cancellable = new Gio.Cancellable(); return this._cancellable; } get device() { if (this._device === undefined) this._device = null; return this._device; } set device(device) { this._device = device; } get plugin() { if (this._plugin === undefined) this._plugin = null; return this._plugin; } set plugin(plugin) { this._plugin = plugin; } }); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/plugins/mousepad.js����������������������������������0000664�0000000�0000000�00000020722�14215434441�0025575�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gdk = imports.gi.Gdk; const GObject = imports.gi.GObject; const Components = imports.service.components; const {InputDialog} = imports.service.ui.mousepad; const PluginBase = imports.service.plugin; var Metadata = { label: _('Mousepad'), description: _('Enables the paired device to act as a remote mouse and keyboard'), id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.Mousepad', incomingCapabilities: [ 'kdeconnect.mousepad.echo', 'kdeconnect.mousepad.request', 'kdeconnect.mousepad.keyboardstate', ], outgoingCapabilities: [ 'kdeconnect.mousepad.echo', 'kdeconnect.mousepad.request', 'kdeconnect.mousepad.keyboardstate', ], actions: { keyboard: { label: _('Keyboard'), icon_name: 'input-keyboard-symbolic', parameter_type: null, incoming: [ 'kdeconnect.mousepad.echo', 'kdeconnect.mousepad.keyboardstate', ], outgoing: ['kdeconnect.mousepad.request'], }, }, }; /** * A map of "KDE Connect" keyvals to Gdk */ const KeyMap = new Map([ [1, Gdk.KEY_BackSpace], [2, Gdk.KEY_Tab], [3, Gdk.KEY_Linefeed], [4, Gdk.KEY_Left], [5, Gdk.KEY_Up], [6, Gdk.KEY_Right], [7, Gdk.KEY_Down], [8, Gdk.KEY_Page_Up], [9, Gdk.KEY_Page_Down], [10, Gdk.KEY_Home], [11, Gdk.KEY_End], [12, Gdk.KEY_Return], [13, Gdk.KEY_Delete], [14, Gdk.KEY_Escape], [15, Gdk.KEY_Sys_Req], [16, Gdk.KEY_Scroll_Lock], [17, 0], [18, 0], [19, 0], [20, 0], [21, Gdk.KEY_F1], [22, Gdk.KEY_F2], [23, Gdk.KEY_F3], [24, Gdk.KEY_F4], [25, Gdk.KEY_F5], [26, Gdk.KEY_F6], [27, Gdk.KEY_F7], [28, Gdk.KEY_F8], [29, Gdk.KEY_F9], [30, Gdk.KEY_F10], [31, Gdk.KEY_F11], [32, Gdk.KEY_F12], ]); /** * Mousepad Plugin * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/mousepad * * TODO: support outgoing mouse events? */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectMousepadPlugin', Properties: { 'state': GObject.ParamSpec.boolean( 'state', 'State', 'Remote keyboard state', GObject.ParamFlags.READABLE, false ), }, }, class Plugin extends PluginBase.Plugin { _init(device) { super._init(device, 'mousepad'); this._input = Components.acquire('input'); this._shareControlChangedId = this.settings.connect( 'changed::share-control', this._sendState.bind(this) ); } get state() { if (this._state === undefined) this._state = false; return this._state; } connected() { super.connected(); this._sendState(); } disconnected() { super.disconnected(); this._state = false; this.notify('state'); } handlePacket(packet) { switch (packet.type) { case 'kdeconnect.mousepad.request': this._handleInput(packet.body); break; case 'kdeconnect.mousepad.echo': this._handleEcho(packet.body); break; case 'kdeconnect.mousepad.keyboardstate': this._handleState(packet); break; } } /** * Handle a input event. * * @param {Object} input - The body of a `kdeconnect.mousepad.request` */ _handleInput(input) { if (!this.settings.get_boolean('share-control')) return; let keysym; let modifiers = 0; // These are ordered, as much as possible, to create the shortest code // path for high-frequency, low-latency events (eg. mouse movement) switch (true) { case input.hasOwnProperty('scroll'): this._input.scrollPointer(input.dx, input.dy); break; case (input.hasOwnProperty('dx') && input.hasOwnProperty('dy')): this._input.movePointer(input.dx, input.dy); break; case (input.hasOwnProperty('key') || input.hasOwnProperty('specialKey')): // NOTE: \u0000 sometimes sent in advance of a specialKey packet if (input.key && input.key === '\u0000') return; // Modifiers if (input.alt) modifiers |= Gdk.ModifierType.MOD1_MASK; if (input.ctrl) modifiers |= Gdk.ModifierType.CONTROL_MASK; if (input.shift) modifiers |= Gdk.ModifierType.SHIFT_MASK; if (input.super) modifiers |= Gdk.ModifierType.SUPER_MASK; // Regular key (printable ASCII or Unicode) if (input.key) { this._input.pressKeys(input.key, modifiers); this._sendEcho(input); // Special key (eg. non-printable ASCII) } else if (input.specialKey && KeyMap.has(input.specialKey)) { keysym = KeyMap.get(input.specialKey); this._input.pressKeys(keysym, modifiers); this._sendEcho(input); } break; case input.hasOwnProperty('singleclick'): this._input.clickPointer(Gdk.BUTTON_PRIMARY); break; case input.hasOwnProperty('doubleclick'): this._input.doubleclickPointer(Gdk.BUTTON_PRIMARY); break; case input.hasOwnProperty('middleclick'): this._input.clickPointer(Gdk.BUTTON_MIDDLE); break; case input.hasOwnProperty('rightclick'): this._input.clickPointer(Gdk.BUTTON_SECONDARY); break; case input.hasOwnProperty('singlehold'): this._input.pressPointer(Gdk.BUTTON_PRIMARY); break; case input.hasOwnProperty('singlerelease'): this._input.releasePointer(Gdk.BUTTON_PRIMARY); break; default: logError(new Error('Unknown input')); } } /** * Handle an echo/ACK of a event we sent, displaying it the dialog entry. * * @param {Object} input - The body of a `kdeconnect.mousepad.echo` */ _handleEcho(input) { if (!this._dialog || !this._dialog.visible) return; // Skip modifiers if (input.alt || input.ctrl || input.super) return; if (input.key) { this._dialog._isAck = true; this._dialog.text.buffer.text += input.key; this._dialog._isAck = false; } else if (KeyMap.get(input.specialKey) === Gdk.KEY_BackSpace) { this._dialog.text.emit('backspace'); } } /** * Handle a state change from the remote keyboard. This is an indication * that the remote keyboard is ready to accept input. * * @param {Object} packet - A `kdeconnect.mousepad.keyboardstate` packet */ _handleState(packet) { this._state = !!packet.body.state; this.notify('state'); } /** * Send an echo/ACK of @input, if requested * * @param {Object} input - The body of a 'kdeconnect.mousepad.request' */ _sendEcho(input) { if (!input.sendAck) return; delete input.sendAck; input.isAck = true; this.device.sendPacket({ type: 'kdeconnect.mousepad.echo', body: input, }); } /** * Send the local keyboard state * * @param {boolean} state - Whether we're ready to accept input */ _sendState() { this.device.sendPacket({ type: 'kdeconnect.mousepad.keyboardstate', body: { state: this.settings.get_boolean('share-control'), }, }); } /** * Open the Keyboard Input dialog */ keyboard() { if (this._dialog === undefined) { this._dialog = new InputDialog({ device: this.device, plugin: this, }); } this._dialog.present(); } destroy() { if (this._input !== undefined) this._input = Components.release('input'); if (this._dialog !== undefined) this._dialog.destroy(); this.settings.disconnect(this._shareControlChangedId); super.destroy(); } }); ����������������������������������������������gnome-shell-extension-gsconnect-50/src/service/plugins/mpris.js�������������������������������������0000664�0000000�0000000�00000062052�14215434441�0025114�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Components = imports.service.components; const Config = imports.config; const DBus = imports.service.utils.dbus; const MPRIS = imports.service.components.mpris; const PluginBase = imports.service.plugin; var Metadata = { label: _('MPRIS'), description: _('Bidirectional remote media playback control'), id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.MPRIS', incomingCapabilities: ['kdeconnect.mpris', 'kdeconnect.mpris.request'], outgoingCapabilities: ['kdeconnect.mpris', 'kdeconnect.mpris.request'], actions: {}, }; /** * MPRIS Plugin * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/mpriscontrol * * See also: * https://specifications.freedesktop.org/mpris-spec/latest/ * https://github.com/GNOME/gnome-shell/blob/master/js/ui/mpris.js */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectMPRISPlugin', }, class Plugin extends PluginBase.Plugin { _init(device) { super._init(device, 'mpris'); this._players = new Map(); this._transferring = new WeakSet(); this._updating = new WeakSet(); this._mpris = Components.acquire('mpris'); this._playerAddedId = this._mpris.connect( 'player-added', this._sendPlayerList.bind(this) ); this._playerRemovedId = this._mpris.connect( 'player-removed', this._sendPlayerList.bind(this) ); this._playerChangedId = this._mpris.connect( 'player-changed', this._onPlayerChanged.bind(this) ); this._playerSeekedId = this._mpris.connect( 'player-seeked', this._onPlayerSeeked.bind(this) ); } connected() { super.connected(); this._requestPlayerList(); this._sendPlayerList(); } disconnected() { super.disconnected(); for (const [identity, player] of this._players) { this._players.delete(identity); player.destroy(); } } handlePacket(packet) { switch (packet.type) { case 'kdeconnect.mpris': this._handleUpdate(packet); break; case 'kdeconnect.mpris.request': this._handleRequest(packet); break; } } /** * Handle a remote player update. * * @param {Core.Packet} packet - A `kdeconnect.mpris` */ _handleUpdate(packet) { try { if (packet.body.hasOwnProperty('playerList')) this._handlePlayerList(packet.body.playerList); else if (packet.body.hasOwnProperty('player')) this._handlePlayerUpdate(packet); } catch (e) { debug(e, this.device.name); } } /** * Handle an updated list of remote players. * * @param {string[]} playerList - A list of remote player names */ _handlePlayerList(playerList) { // Destroy removed players before adding new ones for (const player of this._players.values()) { if (!playerList.includes(player.Identity)) { this._players.delete(player.Identity); player.destroy(); } } for (const identity of playerList) { if (!this._players.has(identity)) { const player = new PlayerRemote(this.device, identity); this._players.set(identity, player); } // Always request player updates; packets are cheap this.device.sendPacket({ type: 'kdeconnect.mpris.request', body: { player: identity, requestNowPlaying: true, requestVolume: true, }, }); } } /** * Handle an update for a remote player. * * @param {Object} packet - A `kdeconnect.mpris` packet */ _handlePlayerUpdate(packet) { const player = this._players.get(packet.body.player); if (player === undefined) return; if (packet.body.hasOwnProperty('transferringAlbumArt')) player.handleAlbumArt(packet); else player.update(packet.body); } /** * Request a list of remote players. */ _requestPlayerList() { this.device.sendPacket({ type: 'kdeconnect.mpris.request', body: { requestPlayerList: true, }, }); } /** * Handle a request for player information or action. * * @param {Core.Packet} packet - a `kdeconnect.mpris.request` * @return {undefined} no return value */ _handleRequest(packet) { // A request for the list of players if (packet.body.hasOwnProperty('requestPlayerList')) return this._sendPlayerList(); // A request for an unknown player; send the list of players if (!this._mpris.hasPlayer(packet.body.player)) return this._sendPlayerList(); // An album art request if (packet.body.hasOwnProperty('albumArtUrl')) return this._sendAlbumArt(packet); // A player command this._handleCommand(packet); } /** * Handle an incoming player command or information request * * @param {Core.Packet} packet - A `kdeconnect.mpris.request` */ async _handleCommand(packet) { if (!this.settings.get_boolean('share-players')) return; let player; try { player = this._mpris.getPlayer(packet.body.player); if (player === undefined || this._updating.has(player)) return; this._updating.add(player); // Player Actions if (packet.body.hasOwnProperty('action')) { switch (packet.body.action) { case 'PlayPause': case 'Play': case 'Pause': case 'Next': case 'Previous': case 'Stop': player[packet.body.action](); break; default: debug(`unknown action: ${packet.body.action}`); } } // Player Properties if (packet.body.hasOwnProperty('setLoopStatus')) player.LoopStatus = packet.body.setLoopStatus; if (packet.body.hasOwnProperty('setShuffle')) player.Shuffle = packet.body.setShuffle; if (packet.body.hasOwnProperty('setVolume')) player.Volume = packet.body.setVolume / 100; if (packet.body.hasOwnProperty('Seek')) await player.Seek(packet.body.Seek * 1000); if (packet.body.hasOwnProperty('SetPosition')) { const offset = (packet.body.SetPosition * 1000) - player.Position; await player.Seek(offset); } // Information Request let hasResponse = false; const response = { type: 'kdeconnect.mpris', body: { player: packet.body.player, }, }; if (packet.body.hasOwnProperty('requestNowPlaying')) { hasResponse = true; Object.assign(response.body, { pos: Math.floor(player.Position / 1000), isPlaying: (player.PlaybackStatus === 'Playing'), canPause: player.CanPause, canPlay: player.CanPlay, canGoNext: player.CanGoNext, canGoPrevious: player.CanGoPrevious, canSeek: player.CanSeek, loopStatus: player.LoopStatus, shuffle: player.Shuffle, // default values for members that will be filled conditionally albumArtUrl: '', length: 0, artist: '', title: '', album: '', nowPlaying: '', volume: 0, }); const metadata = player.Metadata; if (metadata.hasOwnProperty('mpris:artUrl')) { const file = Gio.File.new_for_uri(metadata['mpris:artUrl']); response.body.albumArtUrl = file.get_uri(); } if (metadata.hasOwnProperty('mpris:length')) { const trackLen = Math.floor(metadata['mpris:length'] / 1000); response.body.length = trackLen; } if (metadata.hasOwnProperty('xesam:artist')) { const artists = metadata['xesam:artist']; response.body.artist = artists.join(', '); } if (metadata.hasOwnProperty('xesam:title')) response.body.title = metadata['xesam:title']; if (metadata.hasOwnProperty('xesam:album')) response.body.album = metadata['xesam:album']; // Now Playing if (response.body.artist && response.body.title) { response.body.nowPlaying = [ response.body.artist, response.body.title, ].join(' - '); } else if (response.body.artist) { response.body.nowPlaying = response.body.artist; } else if (response.body.title) { response.body.nowPlaying = response.body.title; } else { response.body.nowPlaying = _('Unknown'); } } if (packet.body.hasOwnProperty('requestVolume')) { hasResponse = true; response.body.volume = Math.floor(player.Volume * 100); } if (hasResponse) this.device.sendPacket(response); } catch (e) { debug(e, this.device.name); } finally { this._updating.delete(player); } } _onPlayerChanged(mpris, player) { if (!this.settings.get_boolean('share-players')) return; this._handleCommand({ body: { player: player.Identity, requestNowPlaying: true, requestVolume: true, }, }); } _onPlayerSeeked(mpris, player, offset) { // TODO: although we can handle full seeked signals, kdeconnect-android // does not, and expects a position update instead this.device.sendPacket({ type: 'kdeconnect.mpris', body: { player: player.Identity, pos: Math.floor(player.Position / 1000), // Seek: Math.floor(offset / 1000), }, }); } async _sendAlbumArt(packet) { let player; try { // Reject concurrent requests for album art player = this._mpris.getPlayer(packet.body.player); if (player === undefined || this._transferring.has(player)) return; // Ensure the requested albumArtUrl matches the current mpris:artUrl const metadata = player.Metadata; if (!metadata.hasOwnProperty('mpris:artUrl')) return; const file = Gio.File.new_for_uri(metadata['mpris:artUrl']); const request = Gio.File.new_for_uri(packet.body.albumArtUrl); if (file.get_uri() !== request.get_uri()) throw RangeError(`invalid URI "${packet.body.albumArtUrl}"`); // Transfer the album art this._transferring.add(player); const transfer = this.device.createTransfer(); transfer.addFile({ type: 'kdeconnect.mpris', body: { transferringAlbumArt: true, player: packet.body.player, albumArtUrl: packet.body.albumArtUrl, }, }, file); await transfer.start(); } catch (e) { debug(e, this.device.name); } finally { this._transferring.delete(player); } } /** * Send the list of player identities and indicate whether we support * transferring album art */ _sendPlayerList() { let playerList = []; if (this.settings.get_boolean('share-players')) playerList = this._mpris.getIdentities(); this.device.sendPacket({ type: 'kdeconnect.mpris', body: { playerList: playerList, supportAlbumArtPayload: true, }, }); } destroy() { if (this._mpris !== undefined) { this._mpris.disconnect(this._playerAddedId); this._mpris.disconnect(this._playerRemovedId); this._mpris.disconnect(this._playerChangedId); this._mpris.disconnect(this._playerSeekedId); this._mpris = Components.release('mpris'); } for (const [identity, player] of this._players) { this._players.delete(identity); player.destroy(); } super.destroy(); } }); /* * A class for mirroring a remote Media Player on DBus */ const MPRISIface = Config.DBUS.lookup_interface('org.mpris.MediaPlayer2'); const MPRISPlayerIface = Config.DBUS.lookup_interface('org.mpris.MediaPlayer2.Player'); const PlayerRemote = GObject.registerClass({ GTypeName: 'GSConnectMPRISPlayerRemote', }, class PlayerRemote extends MPRIS.Player { _init(device, identity) { super._init(); this._device = device; this._Identity = identity; this._isPlaying = false; this._artist = null; this._title = null; this._album = null; this._length = 0; this._artUrl = null; this._ownerId = 0; this._connection = null; this._applicationIface = null; this._playerIface = null; } _getFile(albumArtUrl) { const hash = GLib.compute_checksum_for_string(GLib.ChecksumType.MD5, albumArtUrl, -1); const path = GLib.build_filenamev([Config.CACHEDIR, hash]); return Gio.File.new_for_uri(`file://${path}`); } _requestAlbumArt(state) { if (this._artUrl === state.albumArtUrl) return; const file = this._getFile(state.albumArtUrl); if (file.query_exists(null)) { this._artUrl = file.get_uri(); this._Metadata = undefined; this.notify('Metadata'); } else { this.device.sendPacket({ type: 'kdeconnect.mpris.request', body: { player: this.Identity, albumArtUrl: state.albumArtUrl, }, }); } } _updateMetadata(state) { let metadataChanged = false; if (state.hasOwnProperty('artist')) { if (this._artist !== state.artist) { this._artist = state.artist; metadataChanged = true; } } else if (this._artist) { this._artist = null; metadataChanged = true; } if (state.hasOwnProperty('title')) { if (this._title !== state.title) { this._title = state.title; metadataChanged = true; } } else if (this._title) { this._title = null; metadataChanged = true; } if (state.hasOwnProperty('album')) { if (this._album !== state.album) { this._album = state.album; metadataChanged = true; } } else if (this._album) { this._album = null; metadataChanged = true; } if (state.hasOwnProperty('length')) { if (this._length !== state.length * 1000) { this._length = state.length * 1000; metadataChanged = true; } } else if (this._length) { this._length = 0; metadataChanged = true; } if (state.hasOwnProperty('albumArtUrl')) { this._requestAlbumArt(state); } else if (this._artUrl) { this._artUrl = null; metadataChanged = true; } if (metadataChanged) { this._Metadata = undefined; this.notify('Metadata'); } } async export() { try { if (this._connection === null) { this._connection = await DBus.newConnection(); if (this._applicationIface === null) { this._applicationIface = new DBus.Interface({ g_instance: this, g_connection: this._connection, g_object_path: '/org/mpris/MediaPlayer2', g_interface_info: MPRISIface, }); } if (this._playerIface === null) { this._playerIface = new DBus.Interface({ g_instance: this, g_connection: this._connection, g_object_path: '/org/mpris/MediaPlayer2', g_interface_info: MPRISPlayerIface, }); } } if (this._ownerId !== 0) return; const name = [ this.device.name, this.Identity, ].join('').replace(/[\W]*/g, ''); this._ownerId = Gio.bus_own_name_on_connection( this._connection, `org.mpris.MediaPlayer2.GSConnect.${name}`, Gio.BusNameOwnerFlags.NONE, null, null ); } catch (e) { debug(e, this.Identity); } } unexport() { if (this._ownerId === 0) return; Gio.bus_unown_name(this._ownerId); this._ownerId = 0; } /** * Download album art for the current track of the remote player. * * @param {Core.Packet} packet - A `kdeconnect.mpris` packet */ async handleAlbumArt(packet) { let file; try { file = this._getFile(packet.body.albumArtUrl); // Transfer the album art const transfer = this.device.createTransfer(); transfer.addFile(packet, file); await transfer.start(); this._artUrl = file.get_uri(); this._Metadata = undefined; this.notify('Metadata'); } catch (e) { debug(e, this.device.name); if (file) file.delete_async(GLib.PRIORITY_DEFAULT, null, null); } } /** * Update the internal state of the media player. * * @param {Core.Packet} state - The body of a `kdeconnect.mpris` packet */ update(state) { this.freeze_notify(); // Metadata if (state.hasOwnProperty('nowPlaying')) this._updateMetadata(state); // Playback Status if (state.hasOwnProperty('isPlaying')) { if (this._isPlaying !== state.isPlaying) { this._isPlaying = state.isPlaying; this.notify('PlaybackStatus'); } } if (state.hasOwnProperty('canPlay')) { if (this.CanPlay !== state.canPlay) { this._CanPlay = state.canPlay; this.notify('CanPlay'); } } if (state.hasOwnProperty('canPause')) { if (this.CanPause !== state.canPause) { this._CanPause = state.canPause; this.notify('CanPause'); } } if (state.hasOwnProperty('canGoNext')) { if (this.CanGoNext !== state.canGoNext) { this._CanGoNext = state.canGoNext; this.notify('CanGoNext'); } } if (state.hasOwnProperty('canGoPrevious')) { if (this.CanGoPrevious !== state.canGoPrevious) { this._CanGoPrevious = state.canGoPrevious; this.notify('CanGoPrevious'); } } if (state.hasOwnProperty('pos')) this._Position = state.pos * 1000; if (state.hasOwnProperty('volume')) { if (this.Volume !== state.volume / 100) { this._Volume = state.volume / 100; this.notify('Volume'); } } this.thaw_notify(); if (!this._isPlaying && !this.CanControl) this.unexport(); else this.export(); } /* * Native properties */ get device() { return this._device; } /* * The org.mpris.MediaPlayer2.Player Interface */ get CanControl() { return (this.CanPlay || this.CanPause); } get Metadata() { if (this._Metadata === undefined) { this._Metadata = {}; if (this._artist) { this._Metadata['xesam:artist'] = new GLib.Variant('as', [this._artist]); } if (this._title) { this._Metadata['xesam:title'] = new GLib.Variant('s', this._title); } if (this._album) { this._Metadata['xesam:album'] = new GLib.Variant('s', this._album); } if (this._artUrl) { this._Metadata['mpris:artUrl'] = new GLib.Variant('s', this._artUrl); } this._Metadata['mpris:length'] = new GLib.Variant('x', this._length); } return this._Metadata; } get PlaybackStatus() { if (this._isPlaying) return 'Playing'; return 'Stopped'; } get Volume() { if (this._Volume === undefined) this._Volume = 0.3; return this._Volume; } set Volume(level) { if (this._Volume === level) return; this._Volume = level; this.notify('Volume'); this.device.sendPacket({ type: 'kdeconnect.mpris.request', body: { player: this.Identity, setVolume: Math.floor(this._Volume * 100), }, }); } Next() { if (!this.CanGoNext) return; this.device.sendPacket({ type: 'kdeconnect.mpris.request', body: { player: this.Identity, action: 'Next', }, }); } Pause() { if (!this.CanPause) return; this.device.sendPacket({ type: 'kdeconnect.mpris.request', body: { player: this.Identity, action: 'Pause', }, }); } Play() { if (!this.CanPlay) return; this.device.sendPacket({ type: 'kdeconnect.mpris.request', body: { player: this.Identity, action: 'Play', }, }); } PlayPause() { if (!this.CanPlay && !this.CanPause) return; this.device.sendPacket({ type: 'kdeconnect.mpris.request', body: { player: this.Identity, action: 'PlayPause', }, }); } Previous() { if (!this.CanGoPrevious) return; this.device.sendPacket({ type: 'kdeconnect.mpris.request', body: { player: this.Identity, action: 'Previous', }, }); } Seek(offset) { if (!this.CanSeek) return; this.device.sendPacket({ type: 'kdeconnect.mpris.request', body: { player: this.Identity, Seek: offset, }, }); } SetPosition(trackId, position) { debug(`${this._Identity}: SetPosition(${trackId}, ${position})`); if (!this.CanControl || !this.CanSeek) return; this.device.sendPacket({ type: 'kdeconnect.mpris.request', body: { player: this.Identity, SetPosition: position / 1000, }, }); } Stop() { if (!this.CanControl) return; this.device.sendPacket({ type: 'kdeconnect.mpris.request', body: { player: this.Identity, action: 'Stop', }, }); } destroy() { this.unexport(); if (this._connection) { this._connection.close(null, null); this._connection = null; if (this._applicationIface) { this._applicationIface.destroy(); this._applicationIface = null; } if (this._playerIface) { this._playerIface.destroy(); this._playerIface = null; } } } }); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/plugins/notification.js������������������������������0000664�0000000�0000000�00000055220�14215434441�0026447�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Components = imports.service.components; const Config = imports.config; const PluginBase = imports.service.plugin; const NotificationUI = imports.service.ui.notification; var Metadata = { label: _('Notifications'), description: _('Share notifications with the paired device'), id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.Notification', incomingCapabilities: [ 'kdeconnect.notification', 'kdeconnect.notification.request', ], outgoingCapabilities: [ 'kdeconnect.notification', 'kdeconnect.notification.action', 'kdeconnect.notification.reply', 'kdeconnect.notification.request', ], actions: { withdrawNotification: { label: _('Cancel Notification'), icon_name: 'preferences-system-notifications-symbolic', parameter_type: new GLib.VariantType('s'), incoming: [], outgoing: ['kdeconnect.notification'], }, closeNotification: { label: _('Close Notification'), icon_name: 'preferences-system-notifications-symbolic', parameter_type: new GLib.VariantType('s'), incoming: [], outgoing: ['kdeconnect.notification.request'], }, replyNotification: { label: _('Reply Notification'), icon_name: 'preferences-system-notifications-symbolic', parameter_type: new GLib.VariantType('(ssa{ss})'), incoming: ['kdeconnect.notification'], outgoing: ['kdeconnect.notification.reply'], }, sendNotification: { label: _('Send Notification'), icon_name: 'preferences-system-notifications-symbolic', parameter_type: new GLib.VariantType('a{sv}'), incoming: [], outgoing: ['kdeconnect.notification'], }, activateNotification: { label: _('Activate Notification'), icon_name: 'preferences-system-notifications-symbolic', parameter_type: new GLib.VariantType('(ss)'), incoming: [], outgoing: ['kdeconnect.notification.action'], }, }, }; // A regex for our custom notificaiton ids const ID_REGEX = /^(fdo|gtk)\|([^|]+)\|(.*)$/; // A list of known SMS apps const SMS_APPS = [ // Popular apps that don't contain the string 'sms' 'com.android.messaging', // AOSP 'com.google.android.apps.messaging', // Google Messages 'com.textra', // Textra 'xyz.klinker.messenger', // Pulse 'com.calea.echo', // Mood Messenger 'com.moez.QKSMS', // QKSMS 'rpkandrodev.yaata', // YAATA 'com.tencent.mm', // WeChat 'com.viber.voip', // Viber 'com.kakao.talk', // KakaoTalk 'com.concentriclivers.mms.com.android.mms', // AOSP Clone 'fr.slvn.mms', // AOSP Clone 'com.promessage.message', // 'com.htc.sense.mms', // HTC Messages // Known not to work with sms plugin 'org.thoughtcrime.securesms', // Signal Private Messenger 'com.samsung.android.messaging', // Samsung Messages ]; /** * Try to determine if an notification is from an SMS app * * @param {Core.Packet} packet - A `kdeconnect.notification` * @return {boolean} Whether the notification is from an SMS app */ function _isSmsNotification(packet) { const id = packet.body.id; if (id.includes('sms')) return true; for (let i = 0, len = SMS_APPS.length; i < len; i++) { if (id.includes(SMS_APPS[i])) return true; } return false; } /** * Remove a local libnotify or Gtk notification. * * @param {String|Number} id - Gtk (string) or libnotify id (uint32) * @param {String|null} application - Application Id if Gtk or null */ function _removeNotification(id, application = null) { let name, path, method, variant; if (application !== null) { name = 'org.gtk.Notifications'; method = 'RemoveNotification'; path = '/org/gtk/Notifications'; variant = new GLib.Variant('(ss)', [application, id]); } else { name = 'org.freedesktop.Notifications'; path = '/org/freedesktop/Notifications'; method = 'CloseNotification'; variant = new GLib.Variant('(u)', [id]); } Gio.DBus.session.call( name, path, name, method, variant, null, Gio.DBusCallFlags.NONE, -1, null, (connection, res) => { try { connection.call_finish(res); } catch (e) { logError(e); } } ); } /** * Notification Plugin * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/notifications * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/sendnotifications */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectNotificationPlugin', }, class Plugin extends PluginBase.Plugin { _init(device) { super._init(device, 'notification'); this._listener = Components.acquire('notification'); this._session = Components.acquire('session'); this._notificationAddedId = this._listener.connect( 'notification-added', this._onNotificationAdded.bind(this) ); // Load application notification settings this._applicationsChangedId = this.settings.connect( 'changed::applications', this._onApplicationsChanged.bind(this) ); this._onApplicationsChanged(this.settings, 'applications'); this._applicationsChangedSkip = false; } connected() { super.connected(); this._requestNotifications(); } handlePacket(packet) { switch (packet.type) { case 'kdeconnect.notification': this._handleNotification(packet); break; // TODO case 'kdeconnect.notification.action': this._handleNotificationAction(packet); break; // No Linux/BSD desktop notifications are repliable as yet case 'kdeconnect.notification.reply': debug(`Not implemented: ${packet.type}`); break; case 'kdeconnect.notification.request': this._handleNotificationRequest(packet); break; default: debug(`Unknown notification packet: ${packet.type}`); } } _onApplicationsChanged(settings, key) { if (this._applicationsChangedSkip) return; try { const json = settings.get_string(key); this._applications = JSON.parse(json); } catch (e) { debug(e, this.device.name); this._applicationsChangedSkip = true; settings.set_string(key, '{}'); this._applicationsChangedSkip = false; } } _onNotificationAdded(listener, notification) { try { const notif = notification.full_unpack(); // An unconfigured application if (notif.appName && !this._applications[notif.appName]) { this._applications[notif.appName] = { iconName: 'system-run-symbolic', enabled: true, }; // Store the themed icons for the device preferences window if (notif.icon === undefined) { // Keep default } else if (typeof notif.icon === 'string') { this._applications[notif.appName].iconName = notif.icon; } else if (notif.icon instanceof Gio.ThemedIcon) { const iconName = notif.icon.get_names()[0]; this._applications[notif.appName].iconName = iconName; } this._applicationsChangedSkip = true; this.settings.set_string( 'applications', JSON.stringify(this._applications) ); this._applicationsChangedSkip = false; } // Sending notifications forbidden if (!this.settings.get_boolean('send-notifications')) return; // Sending when the session is active is forbidden if (!this.settings.get_boolean('send-active') && this._session.active) return; // Notifications disabled for this application if (notif.appName && !this._applications[notif.appName].enabled) return; this.sendNotification(notif); } catch (e) { debug(e, this.device.name); } } /** * Handle an incoming notification or closed report. * * FIXME: upstream kdeconnect-android is tagging many notifications as * `silent`, causing them to never be shown. Since we already handle * duplicates in the Shell, we ignore that flag for now. * * @param {Core.Packet} packet - A `kdeconnect.notification` */ _handleNotification(packet) { // A report that a remote notification has been dismissed if (packet.body.hasOwnProperty('isCancel')) this.device.hideNotification(packet.body.id); // A normal, remote notification else this._receiveNotification(packet); } /** * Handle an incoming request to activate a notification action. * * @param {Core.Packet} packet - A `kdeconnect.notification.action` */ _handleNotificationAction(packet) { throw new GObject.NotImplementedError(); } /** * Handle an incoming request to close or list notifications. * * @param {Core.Packet} packet - A `kdeconnect.notification.request` */ _handleNotificationRequest(packet) { // A request for our notifications. This isn't implemented and would be // pretty hard to without communicating with GNOME Shell. if (packet.body.hasOwnProperty('request')) return; // A request to close a local notification // // TODO: kdeconnect-android doesn't send these, and will instead send a // kdeconnect.notification packet with isCancel and an id of "0". // // For clients that do support it, we report notification ids in the // form "type|application-id|notification-id" so we can close it with // the appropriate service. if (packet.body.hasOwnProperty('cancel')) { const [, type, application, id] = ID_REGEX.exec(packet.body.cancel); if (type === 'fdo') _removeNotification(parseInt(id)); else if (type === 'gtk') _removeNotification(id, application); } } /** * Upload an icon from a GLib.Bytes object. * * @param {Core.Packet} packet - The packet for the notification * @param {GLib.Bytes} bytes - The icon bytes */ _uploadBytesIcon(packet, bytes) { const stream = Gio.MemoryInputStream.new_from_bytes(bytes); this._uploadIconStream(packet, stream, bytes.get_size()); } /** * Upload an icon from a Gio.File object. * * @param {Core.Packet} packet - A `kdeconnect.notification` * @param {Gio.File} file - A file object for the icon */ async _uploadFileIcon(packet, file) { const read = new Promise((resolve, reject) => { file.read_async(GLib.PRIORITY_DEFAULT, null, (file, res) => { try { resolve(file.read_finish(res)); } catch (e) { reject(e); } }); }); const query = new Promise((resolve, reject) => { file.query_info_async( 'standard::size', Gio.FileQueryInfoFlags.NONE, GLib.PRIORITY_DEFAULT, null, (file, res) => { try { resolve(file.query_info_finish(res)); } catch (e) { reject(e); } } ); }); const [stream, info] = await Promise.all([read, query]); this._uploadIconStream(packet, stream, info.get_size()); } /** * A function for uploading GThemedIcons * * @param {Core.Packet} packet - The packet for the notification * @param {Gio.ThemedIcon} icon - The GIcon to upload */ _uploadThemedIcon(packet, icon) { const theme = Gtk.IconTheme.get_default(); let file = null; for (const name of icon.names) { // NOTE: kdeconnect-android doesn't support SVGs const size = Math.max.apply(null, theme.get_icon_sizes(name)); const info = theme.lookup_icon(name, size, Gtk.IconLookupFlags.NO_SVG); // Send the first icon we find from the options if (info) { file = Gio.File.new_for_path(info.get_filename()); break; } } if (file) this._uploadFileIcon(packet, file); else this.device.sendPacket(packet); } /** * All icon types end up being uploaded in this function. * * @param {Core.Packet} packet - The packet for the notification * @param {Gio.InputStream} stream - A stream to read the icon bytes from * @param {number} size - Size of the icon in bytes */ async _uploadIconStream(packet, stream, size) { try { const transfer = this.device.createTransfer(); transfer.addStream(packet, stream, size); await transfer.start(); } catch (e) { debug(e); this.device.sendPacket(packet); } } /** * Upload an icon from a GIcon or themed icon name. * * @param {Core.Packet} packet - A `kdeconnect.notification` * @param {Gio.Icon|string|null} icon - An icon or %null * @return {Promise} A promise for the operation */ _uploadIcon(packet, icon = null) { // Normalize strings into GIcons if (typeof icon === 'string') icon = Gio.Icon.new_for_string(icon); if (icon instanceof Gio.ThemedIcon) return this._uploadThemedIcon(packet, icon); if (icon instanceof Gio.FileIcon) return this._uploadFileIcon(packet, icon.get_file()); if (icon instanceof Gio.BytesIcon) return this._uploadBytesIcon(packet, icon.get_bytes()); return this.device.sendPacket(packet); } /** * Send a local notification to the remote device. * * @param {Object} notif - A dictionary of notification parameters * @param {string} notif.appName - The notifying application * @param {string} notif.id - The notification ID * @param {string} notif.title - The notification title * @param {string} notif.body - The notification body * @param {string} notif.ticker - The notification title & body * @param {boolean} notif.isClearable - If the notification can be closed * @param {string|Gio.Icon} notif.icon - An icon name or GIcon */ async sendNotification(notif) { try { const icon = notif.icon || null; delete notif.icon; await this._uploadIcon({ type: 'kdeconnect.notification', body: notif, }, icon); } catch (e) { logError(e); } } async _downloadIcon(packet) { try { if (!packet.hasPayload()) return null; // Save the file in the global cache const path = GLib.build_filenamev([ Config.CACHEDIR, packet.body.payloadHash || `${Date.now()}`, ]); // Check if we've already downloaded this icon // NOTE: if we reject the transfer kdeconnect-android will resend // the notification packet, which may cause problems wrt #789 const file = Gio.File.new_for_path(path); if (file.query_exists(null)) return new Gio.FileIcon({file: file}); // Open the target path and create a transfer const transfer = this.device.createTransfer(); transfer.addFile(packet, file); try { await transfer.start(); return new Gio.FileIcon({file: file}); } catch (e) { debug(e, this.device.name); file.delete_async(GLib.PRIORITY_DEFAULT, null, null); return null; } } catch (e) { debug(e, this.device.name); return null; } } /** * Receive an incoming notification. * * @param {Core.Packet} packet - A `kdeconnect.notification` */ async _receiveNotification(packet) { try { // Set defaults let action = null; let buttons = []; let id = packet.body.id; let title = packet.body.appName; let body = `${packet.body.title}: ${packet.body.text}`; let icon = await this._downloadIcon(packet); // Repliable Notification if (packet.body.requestReplyId) { id = `${packet.body.id}|${packet.body.requestReplyId}`; action = { name: 'replyNotification', parameter: new GLib.Variant('(ssa{ss})', [ packet.body.requestReplyId, '', { appName: packet.body.appName, title: packet.body.title, text: packet.body.text, }, ]), }; } // Notification Actions if (packet.body.actions) { buttons = packet.body.actions.map(action => { return { label: action, action: 'activateNotification', parameter: new GLib.Variant('(ss)', [id, action]), }; }); } // Special case for Missed Calls if (packet.body.id.includes('MissedCall')) { title = packet.body.title; body = packet.body.text; if (icon === null) icon = new Gio.ThemedIcon({name: 'call-missed-symbolic'}); // Special case for SMS notifications } else if (_isSmsNotification(packet)) { title = packet.body.title; body = packet.body.text; action = { name: 'replySms', parameter: new GLib.Variant('s', packet.body.title), }; if (icon === null) icon = new Gio.ThemedIcon({name: 'sms-symbolic'}); // Special case where 'appName' is the same as 'title' } else if (packet.body.appName === packet.body.title) { body = packet.body.text; } // Use the device icon if we still don't have one if (icon === null) icon = new Gio.ThemedIcon({name: this.device.icon_name}); // Show the notification this.device.showNotification({ id: id, title: title, body: body, icon: icon, action: action, buttons: buttons, }); } catch (e) { logError(e); } } /** * Request the remote notifications be sent */ _requestNotifications() { this.device.sendPacket({ type: 'kdeconnect.notification.request', body: {request: true}, }); } /** * Report that a local notification has been closed/dismissed. * TODO: kdeconnect-android doesn't handle incoming isCancel packets. * * @param {string} id - The local notification id */ withdrawNotification(id) { this.device.sendPacket({ type: 'kdeconnect.notification', body: { isCancel: true, id: id, }, }); } /** * Close a remote notification. * TODO: ignore local notifications * * @param {string} id - The remote notification id */ closeNotification(id) { this.device.sendPacket({ type: 'kdeconnect.notification.request', body: {cancel: id}, }); } /** * Reply to a notification sent with a requestReplyId UUID * * @param {string} uuid - The requestReplyId for the repliable notification * @param {string} message - The message to reply with * @param {Object} notification - The original notification packet */ replyNotification(uuid, message, notification) { // If this happens for some reason, things will explode if (!uuid) throw Error('Missing UUID'); // If the message has no content, open a dialog for the user to add one if (!message) { const dialog = new NotificationUI.ReplyDialog({ device: this.device, uuid: uuid, notification: notification, plugin: this, }); dialog.present(); // Otherwise just send the reply } else { this.device.sendPacket({ type: 'kdeconnect.notification.reply', body: { requestReplyId: uuid, message: message, }, }); } } /** * Activate a remote notification action * * @param {string} id - The remote notification id * @param {string} action - The notification action (label) */ activateNotification(id, action) { this.device.sendPacket({ type: 'kdeconnect.notification.action', body: { action: action, key: id, }, }); } destroy() { this.settings.disconnect(this._applicationsChangedId); if (this._listener !== undefined) { this._listener.disconnect(this._notificationAddedId); this._listener = Components.release('notification'); } if (this._session !== undefined) this._session = Components.release('session'); super.destroy(); } }); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/plugins/photo.js�������������������������������������0000664�0000000�0000000�00000015467�14215434441�0025123�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Config = imports.config; const PluginBase = imports.service.plugin; var Metadata = { label: _('Photo'), description: _('Request the paired device to take a photo and transfer it to this PC'), id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.Photo', incomingCapabilities: [ 'kdeconnect.photo', 'kdeconnect.photo.request', ], outgoingCapabilities: [ 'kdeconnect.photo', 'kdeconnect.photo.request', ], actions: { photo: { label: _('Photo'), icon_name: 'camera-photo-symbolic', parameter_type: null, incoming: ['kdeconnect.photo'], outgoing: ['kdeconnect.photo.request'], }, }, }; /** * Photo Plugin * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/photo * * TODO: use Cheese? * check for /dev/video* */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectPhotoPlugin', }, class Plugin extends PluginBase.Plugin { _init(device) { super._init(device, 'photo'); // A reusable launcher for silence procs this._launcher = new Gio.SubprocessLauncher({ flags: (Gio.SubprocessFlags.STDOUT_SILENCE | Gio.SubprocessFlags.STDERR_SILENCE), }); } handlePacket(packet) { switch (packet.type) { case 'kdeconnect.photo': this._receivePhoto(packet); break; case 'kdeconnect.photo.request': this._sendPhoto(packet); break; } } /** * Ensure we have a directory set for storing files that exists. * * @return {string} An absolute directory path */ _ensureReceiveDirectory() { if (this._receiveDir !== undefined) return this._receiveDir; // Ensure a directory is set this._receiveDir = this.settings.get_string('receive-directory'); if (this._receiveDir === '') { this._receiveDir = GLib.get_user_special_dir( GLib.UserDirectory.DIRECTORY_PICTURES ); // Fallback to ~/Pictures const homeDir = GLib.get_home_dir(); if (!this._receiveDir || this._receiveDir === homeDir) { this._receiveDir = GLib.build_filenamev([homeDir, 'Pictures']); this.settings.set_string('receive-directory', this._receiveDir); } } // Ensure the directory exists if (!GLib.file_test(this._receiveDir, GLib.FileTest.IS_DIR)) GLib.mkdir_with_parents(this._receiveDir, 448); return this._receiveDir; } /** * Get a GFile for @filename, while ensuring the directory exists and the * file is unique. * * @param {string} filename - A filename (eg. `image.jpg`) * @return {Gio.File} a file object */ _getFile(filename) { const dirpath = this._ensureReceiveDirectory(); const basepath = GLib.build_filenamev([dirpath, filename]); let filepath = basepath; let copyNum = 0; while (GLib.file_test(filepath, GLib.FileTest.EXISTS)) filepath = `${basepath} (${++copyNum})`; return Gio.File.new_for_path(filepath); } /** * Receive a photo taken by the remote device. * * @param {Core.Packet} packet - a `kdeconnect.photo` */ async _receivePhoto(packet) { let file, transfer; try { // Remote device cancelled the photo operation if (packet.body.hasOwnProperty('cancel')) return; // Open the target path and create a transfer file = this._getFile(packet.body.filename); transfer = this.device.createTransfer(); transfer.addFile(packet, file); // Open the photo if successful, delete on failure await transfer.start(); const uri = file.get_uri(); Gio.AppInfo.launch_default_for_uri_async(uri, null, null, null); } catch (e) { debug(e, this.device.name); if (file) file.delete_async(GLib.PRIORITY_DEFAULT, null, null); } } /** * Take a photo using the Webcam and return the path. * * @param {Core.Packet} packet - A `kdeconnect.photo.request` * @return {Promise<string>} A file path */ _takePhoto(packet) { return new Promise((resolve, reject) => { const time = GLib.DateTime.new_now_local().format('%T'); const path = GLib.build_filenamev([GLib.get_tmp_dir(), `${time}.jpg`]); const proc = this._launcher.spawnv([ Config.FFMPEG_PATH, '-f', 'video4linux2', '-ss', '0:0:2', '-i', '/dev/video0', '-frames', '1', path, ]); proc.wait_check_async(null, (proc, res) => { try { proc.wait_check_finish(res); resolve(path); } catch (e) { reject(e); } }); }); } /** * Send a photo to the remote device. * * @param {Core.Packet} packet - A `kdeconnect.photo.request` */ async _sendPhoto(packet) { if (this.settings.get_boolean('share-camera')) return; let file, transfer; try { // Take a photo const path = await this._takePhoto(); if (path.startsWith('file://')) file = Gio.File.new_for_uri(path); else file = Gio.File.new_for_path(path); // Create the transfer transfer = this.device.createTransfer(); transfer.addFile({ type: 'kdeconnect.photo', body: { filename: file.get_basename(), }, }, file); await transfer.start(); } catch (e) { debug(e, this.device.name); if (transfer) { this.device.showNotification({ id: transfer.uuid, title: _('Transfer Failed'), // TRANSLATORS: eg. Failed to send "photo.jpg" to Google Pixel body: _('Failed to send ā€œ%sā€ to %s').format( file.get_basename(), this.device.name ), icon: new Gio.ThemedIcon({name: 'dialog-warning-symbolic'}), }); } } } /** * Request the remote device begin a photo operation. */ photo() { this.device.sendPacket({ type: 'kdeconnect.photo.request', body: {}, }); } }); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/plugins/ping.js��������������������������������������0000664�0000000�0000000�00000003251�14215434441�0024713�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const PluginBase = imports.service.plugin; var Metadata = { label: _('Ping'), description: _('Send and receive pings'), id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.Ping', incomingCapabilities: ['kdeconnect.ping'], outgoingCapabilities: ['kdeconnect.ping'], actions: { ping: { label: _('Ping'), icon_name: 'dialog-information-symbolic', parameter_type: new GLib.VariantType('s'), incoming: [], outgoing: ['kdeconnect.ping'], }, }, }; /** * Ping Plugin * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/ping */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectPingPlugin', }, class Plugin extends PluginBase.Plugin { _init(device) { super._init(device, 'ping'); } handlePacket(packet) { // Notification const notif = { title: this.device.name, body: _('Ping'), icon: new Gio.ThemedIcon({name: `${this.device.icon_name}`}), }; if (packet.body.message) { // TRANSLATORS: An optional message accompanying a ping, rarely if ever used // eg. Ping: A message sent with ping notif.body = _('Ping: %s').format(packet.body.message); } this.device.showNotification(notif); } ping(message = '') { const packet = { type: 'kdeconnect.ping', body: {}, }; if (message.length) packet.body.message = message; this.device.sendPacket(packet); } }); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/plugins/presenter.js���������������������������������0000664�0000000�0000000�00000002614�14215434441�0025767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const GObject = imports.gi.GObject; const Components = imports.service.components; const PluginBase = imports.service.plugin; var Metadata = { label: _('Presentation'), description: _('Use the paired device as a presenter'), id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.Presenter', incomingCapabilities: ['kdeconnect.presenter'], outgoingCapabilities: [], actions: {}, }; /** * Presenter Plugin * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/presenter * https://github.com/KDE/kdeconnect-android/tree/master/src/org/kde/kdeconnect/Plugins/PresenterPlugin/ */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectPresenterPlugin', }, class Plugin extends PluginBase.Plugin { _init(device) { super._init(device, 'presenter'); this._input = Components.acquire('input'); } handlePacket(packet) { if (packet.body.hasOwnProperty('dx')) { this._input.movePointer( packet.body.dx * 1000, packet.body.dy * 1000 ); } else if (packet.body.stop) { // Currently unsupported and unnecessary as we just re-use the mouse // pointer instead of showing an arbitrary window. } } destroy() { if (this._input !== undefined) this._input = Components.release('input'); super.destroy(); } }); ��������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/plugins/runcommand.js��������������������������������0000664�0000000�0000000�00000016243�14215434441�0026126�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const PluginBase = imports.service.plugin; var Metadata = { label: _('Run Commands'), id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.RunCommand', description: _('Run commands on your paired device or let the device run predefined commands on this PC'), incomingCapabilities: [ 'kdeconnect.runcommand', 'kdeconnect.runcommand.request', ], outgoingCapabilities: [ 'kdeconnect.runcommand', 'kdeconnect.runcommand.request', ], actions: { commands: { label: _('Commands'), icon_name: 'system-run-symbolic', parameter_type: new GLib.VariantType('s'), incoming: ['kdeconnect.runcommand'], outgoing: ['kdeconnect.runcommand.request'], }, executeCommand: { label: _('Commands'), icon_name: 'system-run-symbolic', parameter_type: new GLib.VariantType('s'), incoming: ['kdeconnect.runcommand'], outgoing: ['kdeconnect.runcommand.request'], }, }, }; /** * RunCommand Plugin * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/remotecommands * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/runcommand */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectRunCommandPlugin', Properties: { 'remote-commands': GObject.param_spec_variant( 'remote-commands', 'Remote Command List', 'A list of the device\'s remote commands', new GLib.VariantType('a{sv}'), null, GObject.ParamFlags.READABLE ), }, }, class Plugin extends PluginBase.Plugin { _init(device) { super._init(device, 'runcommand'); // Local Commands this._commandListChangedId = this.settings.connect( 'changed::command-list', this._sendCommandList.bind(this) ); // We cache remote commands so they can be used in the settings even // when the device is offline. this._remote_commands = {}; this.cacheProperties(['_remote_commands']); } get remote_commands() { return this._remote_commands; } connected() { super.connected(); this._sendCommandList(); this._requestCommandList(); this._handleCommandList(this.remote_commands); } clearCache() { this._remote_commands = {}; this.notify('remote-commands'); } cacheLoaded() { if (!this.device.connected) return; this._sendCommandList(); this._requestCommandList(); this._handleCommandList(this.remote_commands); } handlePacket(packet) { switch (packet.type) { case 'kdeconnect.runcommand': this._handleCommandList(packet.body.commandList); break; case 'kdeconnect.runcommand.request': if (packet.body.hasOwnProperty('key')) this._handleCommand(packet.body.key); else if (packet.body.hasOwnProperty('requestCommandList')) this._sendCommandList(); break; } } /** * Handle a request to execute the local command with the UUID @key * * @param {string} key - The UUID of the local command */ _handleCommand(key) { try { const commands = this.settings.get_value('command-list'); const commandList = commands.recursiveUnpack(); if (!commandList.hasOwnProperty(key)) { throw new Gio.IOErrorEnum({ code: Gio.IOErrorEnum.PERMISSION_DENIED, message: `Unknown command: ${key}`, }); } this.device.launchProcess([ '/bin/sh', '-c', commandList[key].command, ]); } catch (e) { logError(e, this.device.name); } } /** * Parse the response to a request for the remote command list. Remove the * command menu if there are no commands, otherwise amend the menu. * * @param {string|Object[]} commandList - A list of remote commands */ _handleCommandList(commandList) { // See: https://github.com/GSConnect/gnome-shell-extension-gsconnect/issues/1051 if (typeof commandList === 'string') { try { commandList = JSON.parse(commandList); } catch (e) { commandList = {}; } } this._remote_commands = commandList; this.notify('remote-commands'); const commandEntries = Object.entries(this.remote_commands); // If there are no commands, hide the menu by disabling the action this.device.lookup_action('commands').enabled = (commandEntries.length > 0); // Commands Submenu const submenu = new Gio.Menu(); for (const [uuid, info] of commandEntries) { const item = new Gio.MenuItem(); item.set_label(info.name); item.set_icon( new Gio.ThemedIcon({name: 'application-x-executable-symbolic'}) ); item.set_detailed_action(`device.executeCommand::${uuid}`); submenu.append_item(item); } // Commands Item const item = new Gio.MenuItem(); item.set_detailed_action('device.commands::menu'); item.set_attribute_value( 'hidden-when', new GLib.Variant('s', 'action-disabled') ); item.set_icon(new Gio.ThemedIcon({name: 'system-run-symbolic'})); item.set_label(_('Commands')); item.set_submenu(submenu); // If the submenu item is already present it will be replaced const menuActions = this.device.settings.get_strv('menu-actions'); const index = menuActions.indexOf('commands'); if (index > -1) { this.device.removeMenuAction('device.commands'); this.device.addMenuItem(item, index); } } /** * Send a request for the remote command list */ _requestCommandList() { this.device.sendPacket({ type: 'kdeconnect.runcommand.request', body: {requestCommandList: true}, }); } /** * Send the local command list */ _sendCommandList() { const commands = this.settings.get_value('command-list').recursiveUnpack(); const commandList = JSON.stringify(commands); this.device.sendPacket({ type: 'kdeconnect.runcommand', body: {commandList: commandList}, }); } /** * Placeholder function for command action */ commands() {} /** * Send a request to execute the remote command with the UUID @key * * @param {string} key - The UUID of the remote command */ executeCommand(key) { this.device.sendPacket({ type: 'kdeconnect.runcommand.request', body: {key: key}, }); } destroy() { this.settings.disconnect(this._commandListChangedId); super.destroy(); } }); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/plugins/sftp.js��������������������������������������0000664�0000000�0000000�00000042373�14215434441�0024742�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Config = imports.config; const Lan = imports.service.backends.lan; const PluginBase = imports.service.plugin; var Metadata = { label: _('SFTP'), id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.SFTP', description: _('Browse the paired device filesystem'), incomingCapabilities: ['kdeconnect.sftp'], outgoingCapabilities: ['kdeconnect.sftp.request'], actions: { mount: { label: _('Mount'), icon_name: 'folder-remote-symbolic', parameter_type: null, incoming: ['kdeconnect.sftp'], outgoing: ['kdeconnect.sftp.request'], }, unmount: { label: _('Unmount'), icon_name: 'media-eject-symbolic', parameter_type: null, incoming: ['kdeconnect.sftp'], outgoing: ['kdeconnect.sftp.request'], }, }, }; const MAX_MOUNT_DIRS = 12; /** * SFTP Plugin * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/sftp * https://github.com/KDE/kdeconnect-android/tree/master/src/org/kde/kdeconnect/Plugins/SftpPlugin */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectSFTPPlugin', }, class Plugin extends PluginBase.Plugin { _init(device) { super._init(device, 'sftp'); this._gmount = null; this._mounting = false; // A reusable launcher for ssh processes this._launcher = new Gio.SubprocessLauncher({ flags: (Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_MERGE), }); // Watch the volume monitor this._volumeMonitor = Gio.VolumeMonitor.get(); this._mountAddedId = this._volumeMonitor.connect( 'mount-added', this._onMountAdded.bind(this) ); this._mountRemovedId = this._volumeMonitor.connect( 'mount-removed', this._onMountRemoved.bind(this) ); } get gmount() { if (this._gmount === null && this.device.connected) { const host = this.device.channel.host; const regex = new RegExp( `sftp://(${host}):(1739|17[4-5][0-9]|176[0-4])` ); for (const mount of this._volumeMonitor.get_mounts()) { const uri = mount.get_root().get_uri(); if (regex.test(uri)) { this._gmount = mount; this._addSubmenu(mount); this._addSymlink(mount); break; } } } return this._gmount; } connected() { super.connected(); // Only enable for Lan connections if (this.device.channel instanceof Lan.Channel) { if (this.settings.get_boolean('automount')) this.mount(); } else { this.device.lookup_action('mount').enabled = false; this.device.lookup_action('unmount').enabled = false; } } handlePacket(packet) { switch (packet.type) { case 'kdeconnect.sftp': if (packet.body.hasOwnProperty('errorMessage')) this._handleError(packet); else this._handleMount(packet); break; } } _onMountAdded(monitor, mount) { if (this._gmount !== null || !this.device.connected) return; const host = this.device.channel.host; const regex = new RegExp(`sftp://(${host}):(1739|17[4-5][0-9]|176[0-4])`); const uri = mount.get_root().get_uri(); if (!regex.test(uri)) return; this._gmount = mount; this._addSubmenu(mount); this._addSymlink(mount); } _onMountRemoved(monitor, mount) { if (this.gmount !== mount) return; this._gmount = null; this._removeSubmenu(); } async _listDirectories(mount) { const file = mount.get_root(); const iter = await new Promise((resolve, reject) => { file.enumerate_children_async( Gio.FILE_ATTRIBUTE_STANDARD_NAME, Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, GLib.PRIORITY_DEFAULT, this.cancellable, (file, res) => { try { resolve(file.enumerate_children_finish(res)); } catch (e) { reject(e); } } ); }); const infos = await new Promise((resolve, reject) => { iter.next_files_async( MAX_MOUNT_DIRS, GLib.PRIORITY_DEFAULT, this.cancellable, (iter, res) => { try { resolve(iter.next_files_finish(res)); } catch (e) { reject(e); } } ); }); iter.close_async(GLib.PRIORITY_DEFAULT, null, null); const directories = {}; for (const info of infos) { const name = info.get_name(); directories[name] = `${file.get_uri()}${name}/`; } return directories; } _onAskQuestion(op, message, choices) { op.reply(Gio.MountOperationResult.HANDLED); } _onAskPassword(op, message, user, domain, flags) { op.reply(Gio.MountOperationResult.HANDLED); } /** * Handle an error reported by the remote device. * * @param {Core.Packet} packet - a `kdeconnect.sftp` */ _handleError(packet) { this.device.showNotification({ id: 'sftp-error', title: _('%s reported an error').format(this.device.name), body: packet.body.errorMessage, icon: new Gio.ThemedIcon({name: 'dialog-error-symbolic'}), priority: Gio.NotificationPriority.HIGH, }); } /** * Mount the remote device using the provided information. * * @param {Core.Packet} packet - a `kdeconnect.sftp` */ async _handleMount(packet) { try { // Already mounted or mounting if (this.gmount !== null || this._mounting) return; this._mounting = true; // Ensure the private key is in the keyring await this._addPrivateKey(); // Create a new mount operation const op = new Gio.MountOperation({ username: packet.body.user || null, password: packet.body.password || null, password_save: Gio.PasswordSave.NEVER, }); op.connect('ask-question', this._onAskQuestion); op.connect('ask-password', this._onAskPassword); // This is the actual call to mount the device const host = this.device.channel.host; const uri = `sftp://${host}:${packet.body.port}/`; const file = Gio.File.new_for_uri(uri); await new Promise((resolve, reject) => { file.mount_enclosing_volume(0, op, null, (file, res) => { try { resolve(file.mount_enclosing_volume_finish(res)); } catch (e) { // Special case when the GMount didn't unmount properly // but is still on the same port and can be reused. if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.ALREADY_MOUNTED)) { resolve(true); // There's a good chance this is a host key verification // error; regardless we'll remove the key for security. } else { this._removeHostKey(host); reject(e); } } }); }); } catch (e) { logError(e, this.device.name); } finally { this._mounting = false; } } /** * Add GSConnect's private key identity to the authentication agent so our * identity can be verified by Android during private key authentication. * * @return {Promise} A promise for the operation */ _addPrivateKey() { const ssh_add = this._launcher.spawnv([ Config.SSHADD_PATH, GLib.build_filenamev([Config.CONFIGDIR, 'private.pem']), ]); return new Promise((resolve, reject) => { ssh_add.communicate_utf8_async(null, null, (proc, res) => { try { const result = proc.communicate_utf8_finish(res)[1].trim(); if (proc.get_exit_status() !== 0) debug(result, this.device.name); resolve(); } catch (e) { reject(e); } }); }); } /** * Remove all host keys from ~/.ssh/known_hosts for @host in the port range * used by KDE Connect (1739-1764). * * @param {string} host - A hostname or IP address */ async _removeHostKey(host) { for (let port = 1739; port <= 1764; port++) { try { const ssh_keygen = this._launcher.spawnv([ Config.SSHKEYGEN_PATH, '-R', `[${host}]:${port}`, ]); await new Promise((resolve, reject) => { ssh_keygen.communicate_utf8_async(null, null, (proc, res) => { try { const stdout = proc.communicate_utf8_finish(res)[1]; const status = proc.get_exit_status(); if (status !== 0) { throw new Gio.IOErrorEnum({ code: Gio.io_error_from_errno(status), message: `${GLib.strerror(status)}\n${stdout}`.trim(), }); } resolve(); } catch (e) { reject(e); } }); }); } catch (e) { logError(e, this.device.name); } } } /* * Mount menu helpers */ _getUnmountSection() { if (this._unmountSection === undefined) { this._unmountSection = new Gio.Menu(); const unmountItem = new Gio.MenuItem(); unmountItem.set_label(Metadata.actions.unmount.label); unmountItem.set_icon(new Gio.ThemedIcon({ name: Metadata.actions.unmount.icon_name, })); unmountItem.set_detailed_action('device.unmount'); this._unmountSection.append_item(unmountItem); } return this._unmountSection; } _getFilesMenuItem() { if (this._filesMenuItem === undefined) { // Files menu icon const emblem = new Gio.Emblem({ icon: new Gio.ThemedIcon({name: 'emblem-default'}), }); const mountedIcon = new Gio.EmblemedIcon({ gicon: new Gio.ThemedIcon({name: 'folder-remote-symbolic'}), }); mountedIcon.add_emblem(emblem); // Files menu item this._filesMenuItem = new Gio.MenuItem(); this._filesMenuItem.set_detailed_action('device.mount'); this._filesMenuItem.set_icon(mountedIcon); this._filesMenuItem.set_label(_('Files')); } return this._filesMenuItem; } async _addSubmenu(mount) { try { const directories = await this._listDirectories(mount); // Submenu sections const dirSection = new Gio.Menu(); const unmountSection = this._getUnmountSection(); for (const [name, uri] of Object.entries(directories)) dirSection.append(name, `device.openPath::${uri}`); // Files submenu const filesSubmenu = new Gio.Menu(); filesSubmenu.append_section(null, dirSection); filesSubmenu.append_section(null, unmountSection); // Files menu item const filesMenuItem = this._getFilesMenuItem(); filesMenuItem.set_submenu(filesSubmenu); // Replace the existing menu item const index = this.device.removeMenuAction('device.mount'); this.device.addMenuItem(filesMenuItem, index); } catch (e) { if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) debug(e, this.device.name); // Reset to allow retrying this._gmount = null; } } _removeSubmenu() { try { const index = this.device.removeMenuAction('device.mount'); const action = this.device.lookup_action('mount'); if (action !== null) { this.device.addMenuAction( action, index, Metadata.actions.mount.label, Metadata.actions.mount.icon_name ); } } catch (e) { logError(e, this.device.name); } } /** * Create a symbolic link referring to the device by name * * @param {Gio.Mount} mount - A GMount to link to */ async _addSymlink(mount) { try { const by_name_dir = Gio.File.new_for_path( `${Config.RUNTIMEDIR}/by-name/` ); try { by_name_dir.make_directory_with_parents(null); } catch (e) { if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS)) throw e; } // Replace path separator with a Unicode lookalike: let safe_device_name = this.device.name.replace('/', 'āˆ•'); if (safe_device_name === '.') safe_device_name = 'Ā·'; else if (safe_device_name === '..') safe_device_name = 'Ā·Ā·'; const link_target = mount.get_root().get_path(); const link = Gio.File.new_for_path( `${by_name_dir.get_path()}/${safe_device_name}` ); // Check for and remove any existing stale link try { const link_stat = await new Promise((resolve, reject) => { link.query_info_async( 'standard::symlink-target', Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, GLib.PRIORITY_DEFAULT, null, (link, res) => { try { resolve(link.query_info_finish(res)); } catch (e) { reject(e); } } ); }); if (link_stat.get_symlink_target() === link_target) return; await new Promise((resolve, reject) => { link.delete_async( GLib.PRIORITY_DEFAULT, null, (link, res) => { try { resolve(link.delete_finish(res)); } catch (e) { reject(e); } } ); }); } catch (e) { if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)) throw e; } link.make_symbolic_link(link_target, null); } catch (e) { debug(e, this.device.name); } } /** * Send a request to mount the remote device */ mount() { if (this.gmount !== null) return; this.device.sendPacket({ type: 'kdeconnect.sftp.request', body: { startBrowsing: true, }, }); } /** * Remove the menu items, unmount the filesystem, replace the mount item */ async unmount() { try { if (this.gmount === null) return; this._removeSubmenu(); this._mounting = false; await new Promise((resolve, reject) => { this.gmount.unmount_with_operation( Gio.MountUnmountFlags.FORCE, new Gio.MountOperation(), null, (mount, res) => { try { resolve(mount.unmount_with_operation_finish(res)); } catch (e) { reject(e); } } ); }); } catch (e) { debug(e, this.device.name); } } destroy() { if (this._volumeMonitor) { this._volumeMonitor.disconnect(this._mountAddedId); this._volumeMonitor.disconnect(this._mountRemovedId); this._volumeMonitor = null; } super.destroy(); } }); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/plugins/share.js�������������������������������������0000664�0000000�0000000�00000036667�14215434441�0025101�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const GdkPixbuf = imports.gi.GdkPixbuf; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const PluginBase = imports.service.plugin; const URI = imports.service.utils.uri; var Metadata = { label: _('Share'), id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.Share', description: _('Share files and URLs between devices'), incomingCapabilities: ['kdeconnect.share.request'], outgoingCapabilities: ['kdeconnect.share.request'], actions: { share: { label: _('Share'), icon_name: 'send-to-symbolic', parameter_type: null, incoming: [], outgoing: ['kdeconnect.share.request'], }, shareFile: { label: _('Share File'), icon_name: 'document-send-symbolic', parameter_type: new GLib.VariantType('(sb)'), incoming: [], outgoing: ['kdeconnect.share.request'], }, shareText: { label: _('Share Text'), icon_name: 'send-to-symbolic', parameter_type: new GLib.VariantType('s'), incoming: [], outgoing: ['kdeconnect.share.request'], }, shareUri: { label: _('Share Link'), icon_name: 'send-to-symbolic', parameter_type: new GLib.VariantType('s'), incoming: [], outgoing: ['kdeconnect.share.request'], }, }, }; /** * Share Plugin * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/share * * TODO: receiving 'text' TODO: Window with textview & 'Copy to Clipboard.. * https://github.com/KDE/kdeconnect-kde/commit/28f11bd5c9a717fb9fbb3f02ddd6cea62021d055 */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectSharePlugin', }, class Plugin extends PluginBase.Plugin { _init(device) { super._init(device, 'share'); } handlePacket(packet) { // TODO: composite jobs (lastModified, numberOfFiles, totalPayloadSize) if (packet.body.hasOwnProperty('filename')) { if (this.settings.get_boolean('receive-files')) this._handleFile(packet); else this._refuseFile(packet); } else if (packet.body.hasOwnProperty('text')) { this._handleText(packet); } else if (packet.body.hasOwnProperty('url')) { this._handleUri(packet); } } _ensureReceiveDirectory() { let receiveDir = this.settings.get_string('receive-directory'); // Ensure a directory is set if (receiveDir.length === 0) { receiveDir = GLib.get_user_special_dir( GLib.UserDirectory.DIRECTORY_DOWNLOAD ); // Fallback to ~/Downloads const homeDir = GLib.get_home_dir(); if (!receiveDir || receiveDir === homeDir) receiveDir = GLib.build_filenamev([homeDir, 'Downloads']); this.settings.set_string('receive-directory', receiveDir); } // Ensure the directory exists if (!GLib.file_test(receiveDir, GLib.FileTest.IS_DIR)) GLib.mkdir_with_parents(receiveDir, 448); return receiveDir; } _getFile(filename) { const dirpath = this._ensureReceiveDirectory(); const basepath = GLib.build_filenamev([dirpath, filename]); let filepath = basepath; let copyNum = 0; while (GLib.file_test(filepath, GLib.FileTest.EXISTS)) filepath = `${basepath} (${++copyNum})`; return Gio.File.new_for_path(filepath); } _refuseFile(packet) { try { this.device.rejectTransfer(packet); this.device.showNotification({ id: `${Date.now()}`, title: _('Transfer Failed'), // TRANSLATORS: eg. Google Pixel is not allowed to upload files body: _('%s is not allowed to upload files').format( this.device.name ), icon: new Gio.ThemedIcon({name: 'dialog-error-symbolic'}), }); } catch (e) { debug(e, this.device.name); } } async _handleFile(packet) { try { const file = this._getFile(packet.body.filename); // Create the transfer const transfer = this.device.createTransfer(); transfer.addFile(packet, file); // Notify that we're about to start the transfer this.device.showNotification({ id: transfer.uuid, title: _('Transferring File'), // TRANSLATORS: eg. Receiving 'book.pdf' from Google Pixel body: _('Receiving ā€œ%sā€ from %s').format( packet.body.filename, this.device.name ), buttons: [{ label: _('Cancel'), action: 'cancelTransfer', parameter: new GLib.Variant('s', transfer.uuid), }], icon: new Gio.ThemedIcon({name: 'document-save-symbolic'}), }); // We'll show a notification (success or failure) let title, body, iconName; let buttons = []; try { await transfer.start(); title = _('Transfer Successful'); // TRANSLATORS: eg. Received 'book.pdf' from Google Pixel body = _('Received ā€œ%sā€ from %s').format( packet.body.filename, this.device.name ); buttons = [ { label: _('Open Folder'), action: 'openPath', parameter: new GLib.Variant('s', file.get_parent().get_uri()), }, { label: _('Open File'), action: 'openPath', parameter: new GLib.Variant('s', file.get_uri()), }, ]; iconName = 'document-save-symbolic'; if (packet.body.open) { const uri = file.get_uri(); Gio.AppInfo.launch_default_for_uri_async(uri, null, null, null); } } catch (e) { debug(e, this.device.name); title = _('Transfer Failed'); // TRANSLATORS: eg. Failed to receive 'book.pdf' from Google Pixel body = _('Failed to receive ā€œ%sā€ from %s').format( packet.body.filename, this.device.name ); iconName = 'dialog-warning-symbolic'; // Clean up the downloaded file on failure file.delete_async(GLib.PRIORITY_DEAFAULT, null, null); } this.device.hideNotification(transfer.uuid); this.device.showNotification({ id: transfer.uuid, title: title, body: body, buttons: buttons, icon: new Gio.ThemedIcon({name: iconName}), }); } catch (e) { logError(e, this.device.name); } } _handleUri(packet) { const uri = packet.body.url; Gio.AppInfo.launch_default_for_uri_async(uri, null, null, null); } _handleText(packet) { const dialog = new Gtk.MessageDialog({ text: _('Text Shared By %s').format(this.device.name), secondary_text: URI.linkify(packet.body.text), secondary_use_markup: true, buttons: Gtk.ButtonsType.CLOSE, }); dialog.message_area.get_children()[1].selectable = true; dialog.set_keep_above(true); dialog.connect('response', (dialog) => dialog.destroy()); dialog.show(); } /** * Open the file chooser dialog for selecting a file or inputing a URI. */ share() { const dialog = new FileChooserDialog(this.device); dialog.show(); } /** * Share local file path or URI * * @param {string} path - Local file path or URI * @param {boolean} open - Whether the file should be opened after transfer */ async shareFile(path, open = false) { try { let file = null; if (path.includes('://')) file = Gio.File.new_for_uri(path); else file = Gio.File.new_for_path(path); // Create the transfer const transfer = this.device.createTransfer(); transfer.addFile({ type: 'kdeconnect.share.request', body: { filename: file.get_basename(), open: open, }, }, file); // Notify that we're about to start the transfer this.device.showNotification({ id: transfer.uuid, title: _('Transferring File'), // TRANSLATORS: eg. Sending 'book.pdf' to Google Pixel body: _('Sending ā€œ%sā€ to %s').format( file.get_basename(), this.device.name ), buttons: [{ label: _('Cancel'), action: 'cancelTransfer', parameter: new GLib.Variant('s', transfer.uuid), }], icon: new Gio.ThemedIcon({name: 'document-send-symbolic'}), }); // We'll show a notification (success or failure) let title, body, iconName; try { await transfer.start(); title = _('Transfer Successful'); // TRANSLATORS: eg. Sent "book.pdf" to Google Pixel body = _('Sent ā€œ%sā€ to %s').format( file.get_basename(), this.device.name ); iconName = 'document-send-symbolic'; } catch (e) { debug(e, this.device.name); title = _('Transfer Failed'); // TRANSLATORS: eg. Failed to send "book.pdf" to Google Pixel body = _('Failed to send ā€œ%sā€ to %s').format( file.get_basename(), this.device.name ); iconName = 'dialog-warning-symbolic'; } this.device.hideNotification(transfer.uuid); this.device.showNotification({ id: transfer.uuid, title: title, body: body, icon: new Gio.ThemedIcon({name: iconName}), }); } catch (e) { debug(e, this.device.name); } } /** * Share a string of text. Remote behaviour is undefined. * * @param {string} text - A string of unicode text */ shareText(text) { this.device.sendPacket({ type: 'kdeconnect.share.request', body: {text: text}, }); } /** * Share a URI. Generally the remote device opens it with the scheme default * * @param {string} uri - A URI to share */ shareUri(uri) { if (GLib.uri_parse_scheme(uri) === 'file') { this.shareFile(uri); return; } this.device.sendPacket({ type: 'kdeconnect.share.request', body: {url: uri}, }); } }); /** A simple FileChooserDialog for sharing files */ var FileChooserDialog = GObject.registerClass({ GTypeName: 'GSConnectShareFileChooserDialog', }, class FileChooserDialog extends Gtk.FileChooserDialog { _init(device) { super._init({ // TRANSLATORS: eg. Send files to Google Pixel title: _('Send files to %s').format(device.name), select_multiple: true, extra_widget: new Gtk.CheckButton({ // TRANSLATORS: Mark the file to be opened once completed label: _('Open when done'), visible: true, }), use_preview_label: false, }); this.device = device; // Align checkbox with sidebar const box = this.get_content_area().get_children()[0].get_children()[0]; const paned = box.get_children()[0]; paned.bind_property( 'position', this.extra_widget, 'margin-left', GObject.BindingFlags.SYNC_CREATE ); // Preview Widget this.preview_widget = new Gtk.Image(); this.preview_widget_active = false; this.connect('update-preview', this._onUpdatePreview); // URI entry this._uriEntry = new Gtk.Entry({ placeholder_text: 'https://', hexpand: true, visible: true, }); this._uriEntry.connect('activate', this._sendLink.bind(this)); // URI/File toggle this._uriButton = new Gtk.ToggleButton({ image: new Gtk.Image({ icon_name: 'web-browser-symbolic', pixel_size: 16, }), valign: Gtk.Align.CENTER, // TRANSLATORS: eg. Send a link to Google Pixel tooltip_text: _('Send a link to %s').format(device.name), visible: true, }); this._uriButton.connect('toggled', this._onUriButtonToggled.bind(this)); this.add_button(_('Cancel'), Gtk.ResponseType.CANCEL); const sendButton = this.add_button(_('Send'), Gtk.ResponseType.OK); sendButton.connect('clicked', this._sendLink.bind(this)); this.get_header_bar().pack_end(this._uriButton); this.set_default_response(Gtk.ResponseType.OK); } _onUpdatePreview(chooser) { try { const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( chooser.get_preview_filename(), chooser.get_scale_factor() * 128, -1 ); chooser.preview_widget.pixbuf = pixbuf; chooser.preview_widget.visible = true; chooser.preview_widget_active = true; } catch (e) { chooser.preview_widget.visible = false; chooser.preview_widget_active = false; } } _onUriButtonToggled(button) { const header = this.get_header_bar(); // Show the URL entry if (button.active) { this.extra_widget.sensitive = false; header.set_custom_title(this._uriEntry); this.set_response_sensitive(Gtk.ResponseType.OK, true); // Hide the URL entry } else { header.set_custom_title(null); this.set_response_sensitive( Gtk.ResponseType.OK, this.get_uris().length > 1 ); this.extra_widget.sensitive = true; } } _sendLink(widget) { if (this._uriButton.active && this._uriEntry.text.length) this.response(1); } vfunc_response(response_id) { if (response_id === Gtk.ResponseType.OK) { for (const uri of this.get_uris()) { const parameter = new GLib.Variant( '(sb)', [uri, this.extra_widget.active] ); this.device.activate_action('shareFile', parameter); } } else if (response_id === 1) { const parameter = new GLib.Variant('s', this._uriEntry.text); this.device.activate_action('shareUri', parameter); } this.destroy(); } }); �������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/plugins/sms.js���������������������������������������0000664�0000000�0000000�00000036041�14215434441�0024563�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const PluginBase = imports.service.plugin; const LegacyMessaging = imports.service.ui.legacyMessaging; const Messaging = imports.service.ui.messaging; const URI = imports.service.utils.uri; var Metadata = { label: _('SMS'), description: _('Send and read SMS of the paired device and be notified of new SMS'), id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.SMS', incomingCapabilities: [ 'kdeconnect.sms.messages', ], outgoingCapabilities: [ 'kdeconnect.sms.request', 'kdeconnect.sms.request_conversation', 'kdeconnect.sms.request_conversations', ], actions: { // SMS Actions sms: { label: _('Messaging'), icon_name: 'sms-symbolic', parameter_type: null, incoming: [], outgoing: ['kdeconnect.sms.request'], }, uriSms: { label: _('New SMS (URI)'), icon_name: 'sms-symbolic', parameter_type: new GLib.VariantType('s'), incoming: [], outgoing: ['kdeconnect.sms.request'], }, replySms: { label: _('Reply SMS'), icon_name: 'sms-symbolic', parameter_type: new GLib.VariantType('s'), incoming: [], outgoing: ['kdeconnect.sms.request'], }, sendMessage: { label: _('Send Message'), icon_name: 'sms-send', parameter_type: new GLib.VariantType('(aa{sv})'), incoming: [], outgoing: ['kdeconnect.sms.request'], }, sendSms: { label: _('Send SMS'), icon_name: 'sms-send', parameter_type: new GLib.VariantType('(ss)'), incoming: [], outgoing: ['kdeconnect.sms.request'], }, shareSms: { label: _('Share SMS'), icon_name: 'sms-send', parameter_type: new GLib.VariantType('s'), incoming: [], outgoing: ['kdeconnect.sms.request'], }, }, }; /** * SMS Message event type. Currently all events are TEXT_MESSAGE. * * TEXT_MESSAGE: Has a "body" field which contains pure, human-readable text */ var MessageEvent = { TEXT_MESSAGE: 0x1, }; /** * SMS Message status. READ/UNREAD match the 'read' field from the Android App * message packet. * * UNREAD: A message not marked as read * READ: A message marked as read */ var MessageStatus = { UNREAD: 0, READ: 1, }; /** * SMS Message type, set from the 'type' field in the Android App * message packet. * * See: https://developer.android.com/reference/android/provider/Telephony.TextBasedSmsColumns.html * * ALL: all messages * INBOX: Received messages * SENT: Sent messages * DRAFT: Message drafts * OUTBOX: Outgoing messages * FAILED: Failed outgoing messages * QUEUED: Messages queued to send later */ var MessageBox = { ALL: 0, INBOX: 1, SENT: 2, DRAFT: 3, OUTBOX: 4, FAILED: 5, QUEUED: 6, }; /** * SMS Plugin * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/sms * https://github.com/KDE/kdeconnect-android/tree/master/src/org/kde/kdeconnect/Plugins/SMSPlugin/ */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectSMSPlugin', Properties: { 'threads': GObject.param_spec_variant( 'threads', 'Conversation List', 'A list of threads', new GLib.VariantType('aa{sv}'), null, GObject.ParamFlags.READABLE ), }, }, class Plugin extends PluginBase.Plugin { _init(device) { super._init(device, 'sms'); this.cacheProperties(['_threads']); } get threads() { if (this._threads === undefined) this._threads = {}; return this._threads; } get window() { if (this.settings.get_boolean('legacy-sms')) { return new LegacyMessaging.Dialog({ device: this.device, plugin: this, }); } if (this._window === undefined) { this._window = new Messaging.Window({ application: Gio.Application.get_default(), device: this.device, plugin: this, }); this._window.connect('destroy', () => { this._window = undefined; }); } return this._window; } clearCache() { this._threads = {}; this.notify('threads'); } cacheLoaded() { this.notify('threads'); } connected() { super.connected(); this._requestConversations(); } handlePacket(packet) { switch (packet.type) { case 'kdeconnect.sms.messages': this._handleMessages(packet.body.messages); break; } } /** * Handle a digest of threads. * * @param {Object[]} messages - A list of message objects * @param {string[]} thread_ids - A list of thread IDs as strings */ _handleDigest(messages, thread_ids) { // Prune threads for (const thread_id of Object.keys(this.threads)) { if (!thread_ids.includes(thread_id)) delete this.threads[thread_id]; } // Request each new or newer thread for (let i = 0, len = messages.length; i < len; i++) { const message = messages[i]; const cache = this.threads[message.thread_id]; if (cache === undefined) { this._requestConversation(message.thread_id); continue; } // If this message is marked read, mark the rest as read if (message.read === MessageStatus.READ) { for (const msg of cache) msg.read = MessageStatus.READ; } // If we don't have a thread for this message or it's newer // than the last message in the cache, request the thread if (!cache.length || cache[cache.length - 1].date < message.date) this._requestConversation(message.thread_id); } this.notify('threads'); } /** * Handle a new single message * * @param {Object} message - A message object */ _handleMessage(message) { let conversation = null; // If the window is open, try and find an active conversation if (this._window) conversation = this._window.getConversationForMessage(message); // If there's an active conversation, we should log the message now if (conversation) conversation.logNext(message); } /** * Parse a conversation (thread of messages) and sort them * * @param {Object[]} thread - A list of sms message objects from a thread */ _handleThread(thread) { // If there are no addresses this will cause major problems... if (!thread[0].addresses || !thread[0].addresses[0]) return; const thread_id = thread[0].thread_id; const cache = this.threads[thread_id] || []; // Handle each message for (let i = 0, len = thread.length; i < len; i++) { const message = thread[i]; // TODO: We only cache messages of a known MessageBox since we // have no reliable way to determine its direction, let alone // what to do with it. if (message.type < 0 || message.type > 6) continue; // If the message exists, just update it const cacheMessage = cache.find(m => m.date === message.date); if (cacheMessage) { Object.assign(cacheMessage, message); } else { cache.push(message); this._handleMessage(message); } } // Sort the thread by ascending date and notify this.threads[thread_id] = cache.sort((a, b) => a.date - b.date); this.notify('threads'); } /** * Handle a response to telephony.request_conversation(s) * * @param {Object[]} messages - A list of sms message objects */ _handleMessages(messages) { try { // If messages is empty there's nothing to do... if (messages.length === 0) return; const thread_ids = []; // Perform some modification of the messages for (let i = 0, len = messages.length; i < len; i++) { const message = messages[i]; // COERCION: thread_id's to strings message.thread_id = `${message.thread_id}`; thread_ids.push(message.thread_id); // TODO: Remove bogus `insert-address-token` entries let a = message.addresses.length; while (a--) { if (message.addresses[a].address === undefined || message.addresses[a].address === 'insert-address-token') message.addresses.splice(a, 1); } } // If there's multiple thread_id's it's a summary of threads if (thread_ids.some(id => id !== thread_ids[0])) this._handleDigest(messages, thread_ids); // Otherwise this is single thread or new message else this._handleThread(messages); } catch (e) { debug(e, this.device.name); } } /** * Request a list of messages from a single thread. * * @param {number} thread_id - The id of the thread to request */ _requestConversation(thread_id) { this.device.sendPacket({ type: 'kdeconnect.sms.request_conversation', body: { threadID: thread_id, }, }); } /** * Request a list of the last message in each unarchived thread. */ _requestConversations() { this.device.sendPacket({ type: 'kdeconnect.sms.request_conversations', }); } /** * A notification action for replying to SMS messages (or missed calls). * * @param {string} hint - Could be either a contact name or phone number */ replySms(hint) { this.window.present(); // FIXME: causes problems now that non-numeric addresses are allowed // this.window.address = hint.toPhoneNumber(); } /** * Send an SMS message * * @param {string} phoneNumber - The phone number to send the message to * @param {string} messageBody - The message to send */ sendSms(phoneNumber, messageBody) { this.device.sendPacket({ type: 'kdeconnect.sms.request', body: { sendSms: true, phoneNumber: phoneNumber, messageBody: messageBody, }, }); } /** * Send a message * * @param {Object[]} addresses - A list of address objects * @param {string} messageBody - The message text * @param {number} [event] - An event bitmask * @param {boolean} [forceSms] - Whether to force SMS * @param {number} [subId] - The SIM card to use */ sendMessage(addresses, messageBody, event = 1, forceSms = false, subId = undefined) { // TODO: waiting on support in kdeconnect-android // if (this._version === 1) { this.device.sendPacket({ type: 'kdeconnect.sms.request', body: { sendSms: true, phoneNumber: addresses[0].address, messageBody: messageBody, }, }); // } else if (this._version === 2) { // this.device.sendPacket({ // type: 'kdeconnect.sms.request', // body: { // version: 2, // addresses: addresses, // messageBody: messageBody, // forceSms: forceSms, // sub_id: subId // } // }); // } } /** * Share a text content by SMS message. This is used by the WebExtension to * share URLs from the browser, but could be used to initiate sharing of any * text content. * * @param {string} url - The link to be shared */ shareSms(url) { // Legacy Mode if (this.settings.get_boolean('legacy-sms')) { const window = this.window; window.present(); window.setMessage(url); // If there are active threads, show the chooser dialog } else if (Object.values(this.threads).length > 0) { const window = new Messaging.ConversationChooser({ application: Gio.Application.get_default(), device: this.device, message: url, plugin: this, }); window.present(); // Otherwise show the window and wait for a contact to be chosen } else { this.window.present(); this.window.setMessage(url, true); } } /** * Open and present the messaging window */ sms() { this.window.present(); } /** * This is the sms: URI scheme handler * * @param {string} uri - The URI the handle (sms:|sms://|sms:///) */ uriSms(uri) { try { uri = new URI.SmsURI(uri); // Lookup contacts const addresses = uri.recipients.map(number => { return {address: number.toPhoneNumber()}; }); const contacts = this.device.contacts.lookupAddresses(addresses); // Present the window and show the conversation const window = this.window; window.present(); window.setContacts(contacts); // Set the outgoing message if the uri has a body variable if (uri.body) window.setMessage(uri.body); } catch (e) { debug(e, `${this.device.name}: "${uri}"`); } } _threadHasAddress(thread, addressObj) { const number = addressObj.address.toPhoneNumber(); for (const taddressObj of thread[0].addresses) { const tnumber = taddressObj.address.toPhoneNumber(); if (number.endsWith(tnumber) || tnumber.endsWith(number)) return true; } return false; } /** * Try to find a thread_id in @smsPlugin for @addresses. * * @param {Object[]} addresses - a list of address objects * @return {string|null} a thread ID */ getThreadIdForAddresses(addresses = []) { const threads = Object.values(this.threads); for (const thread of threads) { if (addresses.length !== thread[0].addresses.length) continue; if (addresses.every(addressObj => this._threadHasAddress(thread, addressObj))) return thread[0].thread_id; } return null; } destroy() { if (this._window !== undefined) this._window.destroy(); super.destroy(); } }); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/plugins/systemvolume.js������������������������������0000664�0000000�0000000�00000012773�14215434441�0026543�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const GObject = imports.gi.GObject; const Components = imports.service.components; const Config = imports.config; const PluginBase = imports.service.plugin; var Metadata = { label: _('System Volume'), description: _('Enable the paired device to control the system volume'), id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.SystemVolume', incomingCapabilities: ['kdeconnect.systemvolume.request'], outgoingCapabilities: ['kdeconnect.systemvolume'], actions: {}, }; /** * SystemVolume Plugin * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/systemvolume * https://github.com/KDE/kdeconnect-android/tree/master/src/org/kde/kdeconnect/Plugins/SystemvolumePlugin/ */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectSystemVolumePlugin', }, class Plugin extends PluginBase.Plugin { _init(device) { super._init(device, 'systemvolume'); // Cache stream properties this._cache = new WeakMap(); // Connect to the mixer try { this._mixer = Components.acquire('pulseaudio'); this._streamChangedId = this._mixer.connect( 'stream-changed', this._sendSink.bind(this) ); this._outputAddedId = this._mixer.connect( 'output-added', this._sendSinkList.bind(this) ); this._outputRemovedId = this._mixer.connect( 'output-removed', this._sendSinkList.bind(this) ); // Modify the error to redirect to the wiki } catch (e) { e.name = _('PulseAudio not found'); e.url = `${Config.PACKAGE_URL}/wiki/Error#pulseaudio-not-found`; throw e; } } handlePacket(packet) { switch (true) { case packet.body.hasOwnProperty('requestSinks'): this._sendSinkList(); break; case packet.body.hasOwnProperty('name'): this._changeSink(packet); break; } } connected() { super.connected(); this._sendSinkList(); } /** * Handle a request to change an output * * @param {Core.Packet} packet - a `kdeconnect.systemvolume.request` */ _changeSink(packet) { let stream; for (const sink of this._mixer.get_sinks()) { if (sink.name === packet.body.name) { stream = sink; break; } } // No sink with the given name if (stream === undefined) { this._sendSinkList(); return; } // Get a cache and store volume and mute states if changed const cache = this._cache.get(stream) || {}; if (packet.body.hasOwnProperty('muted')) { cache.muted = packet.body.muted; this._cache.set(stream, cache); stream.change_is_muted(packet.body.muted); } if (packet.body.hasOwnProperty('volume')) { cache.volume = packet.body.volume; this._cache.set(stream, cache); stream.volume = packet.body.volume; stream.push_volume(); } } /** * Update the cache for @stream * * @param {Gvc.MixerStream} stream - The stream to cache * @return {Object} The updated cache object */ _updateCache(stream) { const state = { name: stream.name, description: stream.display_name, muted: stream.is_muted, volume: stream.volume, maxVolume: this._mixer.get_vol_max_norm(), }; this._cache.set(stream, state); return state; } /** * Send the state of a local sink * * @param {Gvc.MixerControl} mixer - The mixer that owns the stream * @param {number} id - The Id of the stream that changed */ _sendSink(mixer, id) { // Avoid starving the packet channel when fading if (this._mixer.fading) return; // Check the cache const stream = this._mixer.lookup_stream_id(id); const cache = this._cache.get(stream) || {}; // If the port has changed we have to send the whole list to update the // display name if (!cache.display_name || cache.display_name !== stream.display_name) { this._sendSinkList(); return; } // If only volume and/or mute are set, send a single update if (cache.volume !== stream.volume || cache.muted !== stream.is_muted) { // Update the cache const state = this._updateCache(stream); // Send the stream update this.device.sendPacket({ type: 'kdeconnect.systemvolume', body: state, }); } } /** * Send a list of local sinks */ _sendSinkList() { const sinkList = this._mixer.get_sinks().map(sink => { return this._updateCache(sink); }); // Send the sinkList this.device.sendPacket({ type: 'kdeconnect.systemvolume', body: { sinkList: sinkList, }, }); } destroy() { if (this._mixer !== undefined) { this._mixer.disconnect(this._streamChangedId); this._mixer.disconnect(this._outputAddedId); this._mixer.disconnect(this._outputRemovedId); this._mixer = Components.release('pulseaudio'); } super.destroy(); } }); �����gnome-shell-extension-gsconnect-50/src/service/plugins/telephony.js���������������������������������0000664�0000000�0000000�00000015621�14215434441�0025771�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const GdkPixbuf = imports.gi.GdkPixbuf; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Components = imports.service.components; const PluginBase = imports.service.plugin; var Metadata = { label: _('Telephony'), description: _('Be notified about calls and adjust system volume during ringing/ongoing calls'), id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.Telephony', incomingCapabilities: [ 'kdeconnect.telephony', ], outgoingCapabilities: [ 'kdeconnect.telephony.request', 'kdeconnect.telephony.request_mute', ], actions: { muteCall: { // TRANSLATORS: Silence the actively ringing call label: _('Mute Call'), icon_name: 'audio-volume-muted-symbolic', parameter_type: null, incoming: ['kdeconnect.telephony'], outgoing: ['kdeconnect.telephony.request_mute'], }, }, }; /** * Telephony Plugin * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/telephony * https://github.com/KDE/kdeconnect-android/tree/master/src/org/kde/kdeconnect/Plugins/TelephonyPlugin */ var Plugin = GObject.registerClass({ GTypeName: 'GSConnectTelephonyPlugin', }, class Plugin extends PluginBase.Plugin { _init(device) { super._init(device, 'telephony'); // Neither of these are crucial for the plugin to work this._mpris = Components.acquire('mpris'); this._mixer = Components.acquire('pulseaudio'); } handlePacket(packet) { switch (packet.type) { case 'kdeconnect.telephony': this._handleEvent(packet); break; } } /** * Change volume, microphone and media player state in response to an * incoming or answered call. * * @param {string} eventType - 'ringing' or 'talking' */ _setMediaState(eventType) { // Mixer Volume if (this._mixer !== undefined) { switch (this.settings.get_string(`${eventType}-volume`)) { case 'restore': this._mixer.restore(); break; case 'lower': this._mixer.lowerVolume(); break; case 'mute': this._mixer.muteVolume(); break; } if (eventType === 'talking' && this.settings.get_boolean('talking-microphone')) this._mixer.muteMicrophone(); } // Media Playback if (this._mpris && this.settings.get_boolean(`${eventType}-pause`)) this._mpris.pauseAll(); } /** * Restore volume, microphone and media player state (if changed), making * sure to unpause before raising volume. * * TODO: there's a possibility we might revert a media/mixer state set for * another device. */ _restoreMediaState() { // Media Playback if (this._mpris) this._mpris.unpauseAll(); // Mixer Volume if (this._mixer) this._mixer.restore(); } /** * Load a Gdk.Pixbuf from base64 encoded data * * @param {string} data - Base64 encoded JPEG data * @return {Gdk.Pixbuf|null} A contact photo */ _getThumbnailPixbuf(data) { const loader = new GdkPixbuf.PixbufLoader(); try { data = GLib.base64_decode(data); loader.write(data); loader.close(); } catch (e) { debug(e, this.device.name); } return loader.get_pixbuf(); } /** * Handle a telephony event (ringing, talking), showing or hiding a * notification and possibly adjusting the media/mixer state. * * @param {Core.Packet} packet - A `kdeconnect.telephony` */ _handleEvent(packet) { // Only handle 'ringing' or 'talking' events; leave the notification // plugin to handle 'missedCall' since they're often repliable if (!['ringing', 'talking'].includes(packet.body.event)) return; // This is the end of a telephony event if (packet.body.isCancel) this._cancelEvent(packet); else this._notifyEvent(packet); } _cancelEvent(packet) { // Ensure we have a sender // TRANSLATORS: No name or phone number let sender = _('Unknown Contact'); if (packet.body.contactName) sender = packet.body.contactName; else if (packet.body.phoneNumber) sender = packet.body.phoneNumber; this.device.hideNotification(`${packet.body.event}|${sender}`); this._restoreMediaState(); } _notifyEvent(packet) { let body; let buttons = []; let icon = null; let priority = Gio.NotificationPriority.NORMAL; // Ensure we have a sender // TRANSLATORS: No name or phone number let sender = _('Unknown Contact'); if (packet.body.contactName) sender = packet.body.contactName; else if (packet.body.phoneNumber) sender = packet.body.phoneNumber; // If there's a photo, use it as the notification icon if (packet.body.phoneThumbnail) icon = this._getThumbnailPixbuf(packet.body.phoneThumbnail); if (icon === null) icon = new Gio.ThemedIcon({name: 'call-start-symbolic'}); // Notify based based on the event type if (packet.body.event === 'ringing') { this._setMediaState('ringing'); // TRANSLATORS: The phone is ringing body = _('Incoming call'); buttons = [{ action: 'muteCall', // TRANSLATORS: Silence the actively ringing call label: _('Mute'), parameter: null, }]; priority = Gio.NotificationPriority.URGENT; } if (packet.body.event === 'talking') { this.device.hideNotification(`ringing|${sender}`); this._setMediaState('talking'); // TRANSLATORS: A phone call is active body = _('Ongoing call'); } this.device.showNotification({ id: `${packet.body.event}|${sender}`, title: sender, body: body, icon: icon, priority: priority, buttons: buttons, }); } /** * Silence an incoming call and restore the previous mixer/media state, if * applicable. */ muteCall() { this.device.sendPacket({ type: 'kdeconnect.telephony.request_mute', body: {}, }); this._restoreMediaState(); } destroy() { if (this._mixer !== undefined) this._mixer = Components.release('pulseaudio'); if (this._mpris !== undefined) this._mpris = Components.release('mpris'); super.destroy(); } }); ���������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/ui/��������������������������������������������������0000775�0000000�0000000�00000000000�14215434441�0022353�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/ui/__init__.js���������������������������������������0000664�0000000�0000000�00000002432�14215434441�0024451�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gdk = imports.gi.Gdk; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Gtk = imports.gi.Gtk; const Config = imports.config; /* * Window State */ Gtk.Window.prototype.restoreGeometry = function (context = 'default') { this._windowState = new Gio.Settings({ settings_schema: Config.GSCHEMA.lookup( 'org.gnome.Shell.Extensions.GSConnect.WindowState', true ), path: `/org/gnome/shell/extensions/gsconnect/${context}/`, }); // Size const [width, height] = this._windowState.get_value('window-size').deepUnpack(); if (width && height) this.set_default_size(width, height); // Maximized State if (this._windowState.get_boolean('window-maximized')) this.maximize(); }; Gtk.Window.prototype.saveGeometry = function () { const state = this.get_window().get_state(); // Maximized State const maximized = (state & Gdk.WindowState.MAXIMIZED); this._windowState.set_boolean('window-maximized', maximized); // Leave the size at the value before maximizing if (maximized || (state & Gdk.WindowState.FULLSCREEN)) return; // Size const size = this.get_size(); this._windowState.set_value('window-size', new GLib.Variant('(ii)', size)); }; ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/ui/contacts.js���������������������������������������0000664�0000000�0000000�00000042716�14215434441�0024541�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gdk = imports.gi.Gdk; const GdkPixbuf = imports.gi.GdkPixbuf; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; /** * Return a random color * * @param {*} [salt] - If not %null, will be used as salt for generating a color * @param {number} alpha - A value in the [0...1] range for the alpha channel * @return {Gdk.RGBA} A new Gdk.RGBA object generated from the input */ function randomRGBA(salt = null, alpha = 1.0) { let red, green, blue; if (salt !== null) { const hash = new GLib.Variant('s', `${salt}`).hash(); red = ((hash & 0xFF0000) >> 16) / 255; green = ((hash & 0x00FF00) >> 8) / 255; blue = (hash & 0x0000FF) / 255; } else { red = Math.random(); green = Math.random(); blue = Math.random(); } return new Gdk.RGBA({red: red, green: green, blue: blue, alpha: alpha}); } /** * Get the relative luminance of a RGB set * See: https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef * * @param {Gdk.RGBA} rgba - A GdkRGBA object * @return {number} The relative luminance of the color */ function relativeLuminance(rgba) { const {red, green, blue} = rgba; const R = (red > 0.03928) ? red / 12.92 : Math.pow(((red + 0.055) / 1.055), 2.4); const G = (green > 0.03928) ? green / 12.92 : Math.pow(((green + 0.055) / 1.055), 2.4); const B = (blue > 0.03928) ? blue / 12.92 : Math.pow(((blue + 0.055) / 1.055), 2.4); return 0.2126 * R + 0.7152 * G + 0.0722 * B; } /** * Get a GdkRGBA contrasted for the input * See: https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef * * @param {Gdk.RGBA} rgba - A GdkRGBA object for the background color * @return {Gdk.RGBA} A GdkRGBA object for the foreground color */ function getFgRGBA(rgba) { const bgLuminance = relativeLuminance(rgba); const lightContrast = (0.07275541795665634 + 0.05) / (bgLuminance + 0.05); const darkContrast = (bgLuminance + 0.05) / (0.0046439628482972135 + 0.05); const value = (darkContrast > lightContrast) ? 0.06 : 0.94; return new Gdk.RGBA({red: value, green: value, blue: value, alpha: 0.5}); } /** * Get a GdkPixbuf for @path, allowing the corrupt JPEG's KDE Connect sometimes * sends. This function is synchronous. * * @param {string} path - A local file path * @param {number} size - Size in pixels * @param {scale} [scale] - Scale factor for the size * @return {Gdk.Pixbuf} A pixbuf */ function getPixbufForPath(path, size, scale = 1.0) { let data, loader; // Catch missing avatar files try { data = GLib.file_get_contents(path)[1]; } catch (e) { debug(e, path); return undefined; } // Consider errors from partially corrupt JPEGs to be warnings try { loader = new GdkPixbuf.PixbufLoader(); loader.write(data); loader.close(); } catch (e) { debug(e, path); } const pixbuf = loader.get_pixbuf(); // Scale to monitor size = Math.floor(size * scale); return pixbuf.scale_simple(size, size, GdkPixbuf.InterpType.HYPER); } function getPixbufForIcon(name, size, scale, bgColor) { const color = getFgRGBA(bgColor); const theme = Gtk.IconTheme.get_default(); const info = theme.lookup_icon_for_scale( name, size, scale, Gtk.IconLookupFlags.FORCE_SYMBOLIC ); return info.load_symbolic(color, null, null, null)[0]; } /** * Return a localized string for a phone number type * See: http://www.ietf.org/rfc/rfc2426.txt * * @param {string} type - An RFC2426 phone number type * @return {string} A localized string like 'Mobile' */ function getNumberTypeLabel(type) { if (type.includes('fax')) // TRANSLATORS: A fax number return _('Fax'); if (type.includes('work')) // TRANSLATORS: A work or office phone number return _('Work'); if (type.includes('cell')) // TRANSLATORS: A mobile or cellular phone number return _('Mobile'); if (type.includes('home')) // TRANSLATORS: A home phone number return _('Home'); // TRANSLATORS: All other phone number types return _('Other'); } /** * Get a display number from @contact for @address. * * @param {Object} contact - A contact object * @param {string} address - A phone number * @return {string} A (possibly) better display number for the address */ function getDisplayNumber(contact, address) { const number = address.toPhoneNumber(); for (const contactNumber of contact.numbers) { const cnumber = contactNumber.value.toPhoneNumber(); if (number.endsWith(cnumber) || cnumber.endsWith(number)) return GLib.markup_escape_text(contactNumber.value, -1); } return GLib.markup_escape_text(address, -1); } /** * Contact Avatar */ const AvatarCache = new WeakMap(); var Avatar = GObject.registerClass({ GTypeName: 'GSConnectContactAvatar', }, class ContactAvatar extends Gtk.DrawingArea { _init(contact = null) { super._init({ height_request: 32, width_request: 32, valign: Gtk.Align.CENTER, visible: true, }); this.contact = contact; } get rgba() { if (this._rgba === undefined) { if (this.contact) this._rgba = randomRGBA(this.contact.name); else this._rgba = randomRGBA(GLib.uuid_string_random()); } return this._rgba; } get contact() { if (this._contact === undefined) this._contact = null; return this._contact; } set contact(contact) { if (this.contact === contact) return; this._contact = contact; this._surface = undefined; this._rgba = undefined; this._offset = 0; } _loadSurface() { // Get the monitor scale const display = Gdk.Display.get_default(); const monitor = display.get_monitor_at_window(this.get_window()); const scale = monitor.get_scale_factor(); // If there's a contact with an avatar, try to load it if (this.contact && this.contact.avatar) { // Check the cache this._surface = AvatarCache.get(this.contact); // Try loading the pixbuf if (!this._surface) { const pixbuf = getPixbufForPath( this.contact.avatar, this.width_request, scale ); if (pixbuf) { this._surface = Gdk.cairo_surface_create_from_pixbuf( pixbuf, 0, this.get_window() ); AvatarCache.set(this.contact, this._surface); } } } // If we still don't have a surface, load a fallback if (!this._surface) { let iconName; // If we were given a contact, it's direct message otherwise group if (this.contact) iconName = 'avatar-default-symbolic'; else iconName = 'group-avatar-symbolic'; // Center the icon this._offset = (this.width_request - 24) / 2; // Load the fallback const pixbuf = getPixbufForIcon(iconName, 24, scale, this.rgba); this._surface = Gdk.cairo_surface_create_from_pixbuf( pixbuf, 0, this.get_window() ); } } vfunc_draw(cr) { if (!this._surface) this._loadSurface(); // Clip to a circle const rad = this.width_request / 2; cr.arc(rad, rad, rad, 0, 2 * Math.PI); cr.clipPreserve(); // Fill the background if the the surface is offset if (this._offset > 0) { Gdk.cairo_set_source_rgba(cr, this.rgba); cr.fill(); } // Draw the avatar/icon cr.setSourceSurface(this._surface, this._offset, this._offset); cr.paint(); cr.$dispose(); return Gdk.EVENT_PROPAGATE; } }); /** * A row for a contact address (usually a phone number). */ const AddressRow = GObject.registerClass({ GTypeName: 'GSConnectContactsAddressRow', Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/contacts-address-row.ui', Children: ['avatar', 'name-label', 'address-label', 'type-label'], }, class AddressRow extends Gtk.ListBoxRow { _init(contact, index = 0) { super._init(); this._index = index; this._number = contact.numbers[index]; this.contact = contact; } get contact() { if (this._contact === undefined) this._contact = null; return this._contact; } set contact(contact) { if (this.contact === contact) return; this._contact = contact; if (this._index === 0) { this.avatar.contact = contact; this.avatar.visible = true; this.name_label.label = GLib.markup_escape_text(contact.name, -1); this.name_label.visible = true; this.address_label.margin_start = 0; this.address_label.margin_end = 0; } else { this.avatar.visible = false; this.name_label.visible = false; // TODO: rtl inverts margin-start so the number don't align this.address_label.margin_start = 38; this.address_label.margin_end = 38; } this.address_label.label = GLib.markup_escape_text(this.number.value, -1); if (this.number.type !== undefined) this.type_label.label = getNumberTypeLabel(this.number.type); } get number() { if (this._number === undefined) return {value: 'unknown', type: 'unknown'}; return this._number; } }); /** * A widget for selecting contact addresses (usually phone numbers) */ var ContactChooser = GObject.registerClass({ GTypeName: 'GSConnectContactChooser', Properties: { 'device': GObject.ParamSpec.object( 'device', 'Device', 'The device associated with this window', GObject.ParamFlags.READWRITE, GObject.Object ), 'store': GObject.ParamSpec.object( 'store', 'Store', 'The contacts store', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, GObject.Object ), }, Signals: { 'number-selected': { flags: GObject.SignalFlags.RUN_FIRST, param_types: [GObject.TYPE_STRING], }, }, Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/contact-chooser.ui', Children: ['entry', 'list', 'scrolled'], }, class ContactChooser extends Gtk.Grid { _init(params) { super._init(params); // Setup the contact list this.list._entry = this.entry.text; this.list.set_filter_func(this._filter); this.list.set_sort_func(this._sort); // Make sure we're using the correct contacts store this.device.bind_property( 'contacts', this, 'store', GObject.BindingFlags.SYNC_CREATE ); // Cleanup on ::destroy this.connect('destroy', this._onDestroy); } get store() { if (this._store === undefined) this._store = null; return this._store; } set store(store) { if (this.store === store) return; // Unbind the old store if (this._store) { // Disconnect from the store this._store.disconnect(this._contactAddedId); this._store.disconnect(this._contactRemovedId); this._store.disconnect(this._contactChangedId); // Clear the contact list const rows = this.list.get_children(); for (let i = 0, len = rows.length; i < len; i++) { rows[i].destroy(); // HACK: temporary mitigator for mysterious GtkListBox leak imports.system.gc(); } } // Set the store this._store = store; // Bind the new store if (this._store) { // Connect to the new store this._contactAddedId = store.connect( 'contact-added', this._onContactAdded.bind(this) ); this._contactRemovedId = store.connect( 'contact-removed', this._onContactRemoved.bind(this) ); this._contactChangedId = store.connect( 'contact-changed', this._onContactChanged.bind(this) ); // Populate the list this._populate(); } } /* * ContactStore Callbacks */ _onContactAdded(store, id) { const contact = this.store.get_contact(id); this._addContact(contact); } _onContactRemoved(store, id) { const rows = this.list.get_children(); for (let i = 0, len = rows.length; i < len; i++) { const row = rows[i]; if (row.contact.id === id) { row.destroy(); // HACK: temporary mitigator for mysterious GtkListBox leak imports.system.gc(); } } } _onContactChanged(store, id) { this._onContactRemoved(store, id); this._onContactAdded(store, id); } _onDestroy(chooser) { chooser.store = null; } _onSearchChanged(entry) { this.list._entry = entry.text; let dynamic = this.list.get_row_at_index(0); // If the entry contains string with 2 or more digits... if (entry.text.replace(/\D/g, '').length >= 2) { // ...ensure we have a dynamic contact for it if (!dynamic || !dynamic.__tmp) { dynamic = new AddressRow({ // TRANSLATORS: A phone number (eg. "Send to 555-5555") name: _('Send to %s').format(entry.text), numbers: [{type: 'unknown', value: entry.text}], }); dynamic.__tmp = true; this.list.add(dynamic); // ...or if we already do, then update it } else { const address = entry.text; // Update contact object dynamic.contact.name = address; dynamic.contact.numbers[0].value = address; // Update UI dynamic.name_label.label = _('Send to %s').format(address); dynamic.address_label.label = address; } // ...otherwise remove any dynamic contact that's been created } else if (dynamic && dynamic.__tmp) { dynamic.destroy(); } this.list.invalidate_filter(); this.list.invalidate_sort(); } // GtkListBox::row-activated _onNumberSelected(box, row) { if (row === null) return; // Emit the number const address = row.number.value; this.emit('number-selected', address); // Reset the contact list this.entry.text = ''; this.list.select_row(null); this.scrolled.vadjustment.value = 0; } _filter(row) { // Dynamic contact always shown if (row.__tmp) return true; const query = row.get_parent()._entry; // Show contact if text is substring of name const queryName = query.toLocaleLowerCase(); if (row.contact.name.toLocaleLowerCase().includes(queryName)) return true; // Show contact if text is substring of number const queryNumber = query.toPhoneNumber(); if (queryNumber.length) { for (const number of row.contact.numbers) { if (number.value.toPhoneNumber().includes(queryNumber)) return true; } // Query is effectively empty } else if (/^0+/.test(query)) { return true; } return false; } _sort(row1, row2) { if (row1.__tmp) return -1; if (row2.__tmp) return 1; return row1.contact.name.localeCompare(row2.contact.name); } _populate() { // Add each contact const contacts = this.store.contacts; for (let i = 0, len = contacts.length; i < len; i++) this._addContact(contacts[i]); } _addContactNumber(contact, index) { const row = new AddressRow(contact, index); this.list.add(row); return row; } _addContact(contact) { try { // HACK: fix missing contact names if (contact.name === undefined) contact.name = _('Unknown Contact'); if (contact.numbers.length === 1) return this._addContactNumber(contact, 0); for (let i = 0, len = contact.numbers.length; i < len; i++) this._addContactNumber(contact, i); } catch (e) { logError(e); } } /** * Get a dictionary of number-contact pairs for each selected phone number. * * @return {Object[]} A dictionary of contacts */ getSelected() { try { const selected = {}; for (const row of this.list.get_selected_rows()) selected[row.number.value] = row.contact; return selected; } catch (e) { logError(e); return {}; } } }); ��������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/ui/legacyMessaging.js��������������������������������0000664�0000000�0000000�00000013722�14215434441�0026020�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Contacts = imports.service.ui.contacts; const Messaging = imports.service.ui.messaging; const URI = imports.service.utils.uri; var Dialog = GObject.registerClass({ GTypeName: 'GSConnectLegacyMessagingDialog', Properties: { 'device': GObject.ParamSpec.object( 'device', 'Device', 'The device associated with this window', GObject.ParamFlags.READWRITE, GObject.Object ), 'plugin': GObject.ParamSpec.object( 'plugin', 'Plugin', 'The plugin providing messages', GObject.ParamFlags.READWRITE, GObject.Object ), }, Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/legacy-messaging-dialog.ui', Children: [ 'infobar', 'stack', 'message-box', 'message-avatar', 'message-label', 'entry', ], }, class Dialog extends Gtk.Dialog { _init(params) { super._init({ application: Gio.Application.get_default(), device: params.device, plugin: params.plugin, use_header_bar: true, }); this.set_response_sensitive(Gtk.ResponseType.OK, false); // Dup some functions this.headerbar = this.get_titlebar(); this._setHeaderBar = Messaging.Window.prototype._setHeaderBar; // Info bar this.device.bind_property( 'connected', this.infobar, 'reveal-child', GObject.BindingFlags.INVERT_BOOLEAN ); // Message Entry/Send Button this.device.bind_property( 'connected', this.entry, 'sensitive', GObject.BindingFlags.DEFAULT ); this._connectedId = this.device.connect( 'notify::connected', this._onStateChanged.bind(this) ); this._entryChangedId = this.entry.buffer.connect( 'changed', this._onStateChanged.bind(this) ); // Set the message if given if (params.message) { this.message = params.message; this.addresses = params.message.addresses; this.message_avatar.contact = this.device.contacts.query({ number: this.addresses[0].address, }); this.message_label.label = URI.linkify(this.message.body); this.message_box.visible = true; // Otherwise set the address(es) if we were passed those } else if (params.addresses) { this.addresses = params.addresses; } // Load the contact list if we weren't supplied with an address if (this.addresses.length === 0) { this.contact_chooser = new Contacts.ContactChooser({ device: this.device, }); this.stack.add_named(this.contact_chooser, 'contact-chooser'); this.stack.child_set_property(this.contact_chooser, 'position', 0); this._numberSelectedId = this.contact_chooser.connect( 'number-selected', this._onNumberSelected.bind(this) ); this.stack.visible_child_name = 'contact-chooser'; } this.restoreGeometry('legacy-messaging-dialog'); this.connect('destroy', this._onDestroy); } _onDestroy(dialog) { if (dialog._numberSelectedId !== undefined) { dialog.contact_chooser.disconnect(dialog._numberSelectedId); dialog.contact_chooser.destroy(); } dialog.entry.buffer.disconnect(dialog._entryChangedId); dialog.device.disconnect(dialog._connectedId); } vfunc_delete_event() { this.saveGeometry(); return false; } vfunc_response(response_id) { if (response_id === Gtk.ResponseType.OK) { // Refuse to send empty or whitespace only texts if (!this.entry.buffer.text.trim()) return; this.plugin.sendMessage( this.addresses, this.entry.buffer.text, 1, true ); } this.destroy(); } get addresses() { if (this._addresses === undefined) this._addresses = []; return this._addresses; } set addresses(addresses = []) { this._addresses = addresses; // Set the headerbar this._setHeaderBar(this._addresses); // Show the message editor this.stack.visible_child_name = 'message-editor'; this._onStateChanged(); } get device() { if (this._device === undefined) this._device = null; return this._device; } set device(device) { this._device = device; } get plugin() { if (this._plugin === undefined) this._plugin = null; return this._plugin; } set plugin(plugin) { this._plugin = plugin; } _onActivateLink(label, uri) { Gtk.show_uri_on_window( this.get_toplevel(), uri.includes('://') ? uri : `https://${uri}`, Gtk.get_current_event_time() ); return true; } _onNumberSelected(chooser, number) { const contacts = chooser.getSelected(); this.addresses = Object.keys(contacts).map(address => { return {address: address}; }); } _onStateChanged() { if (this.device.connected && this.entry.buffer.text.trim() && this.stack.visible_child_name === 'message-editor') this.set_response_sensitive(Gtk.ResponseType.OK, true); else this.set_response_sensitive(Gtk.ResponseType.OK, false); } /** * Set the contents of the message entry * * @param {string} text - The message to place in the entry */ setMessage(text) { this.entry.buffer.text = text; } }); ����������������������������������������������gnome-shell-extension-gsconnect-50/src/service/ui/messaging.js��������������������������������������0000664�0000000�0000000�00000113042�14215434441�0024667�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Tweener = imports.tweener.tweener; const Gdk = imports.gi.Gdk; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Pango = imports.gi.Pango; const Contacts = imports.service.ui.contacts; const Sms = imports.service.plugins.sms; const URI = imports.service.utils.uri; /* * Useful time constants */ const TIME_SPAN_MINUTE = 60000; const TIME_SPAN_HOUR = 3600000; const TIME_SPAN_DAY = 86400000; const TIME_SPAN_WEEK = 604800000; // Less than an hour (eg. 42 minutes ago) const _lthLong = new Intl.RelativeTimeFormat('default', { numeric: 'auto', style: 'long', }); // Less than a day ago (eg. 11:42 PM) const _ltdFormat = new Intl.DateTimeFormat('default', { hour: 'numeric', minute: 'numeric', }); // Less than a week ago (eg. Monday) const _ltwLong = new Intl.DateTimeFormat('default', { weekday: 'long', }); // Less than a week ago (eg. Mon) const _ltwShort = new Intl.DateTimeFormat('default', { weekday: 'short', }); // Less than a year (eg. Oct 31) const _ltyShort = new Intl.DateTimeFormat('default', { day: 'numeric', month: 'short', }); // Less than a year (eg. October 31) const _ltyLong = new Intl.DateTimeFormat('default', { day: 'numeric', month: 'long', }); // Greater than a year (eg. October 31, 2019) const _gtyLong = new Intl.DateTimeFormat('default', { day: 'numeric', month: 'long', year: 'numeric', }); // Greater than a year (eg. 10/31/2019) const _gtyShort = new Intl.DateTimeFormat('default', { day: 'numeric', month: 'numeric', year: 'numeric', }); // Pretty close to strftime's %c const _cFormat = new Intl.DateTimeFormat('default', { year: 'numeric', month: 'short', day: 'numeric', weekday: 'short', hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'short', }); /** * Return a human-readable timestamp, formatted for longer contexts. * * @param {number} time - Milliseconds since the epoch (local time) * @return {string} A localized timestamp similar to what Android Messages uses */ function getTime(time) { const date = new Date(time); const now = new Date(); const diff = now - time; // Super recent if (diff < TIME_SPAN_MINUTE) // TRANSLATORS: Less than a minute ago return _('Just now'); // Under an hour (TODO: these labels aren't updated) if (diff < TIME_SPAN_HOUR) return _lthLong.format(-Math.floor(diff / TIME_SPAN_MINUTE), 'minute'); // Yesterday, but less than 24 hours ago if (diff < TIME_SPAN_DAY && now.getDay() !== date.getDay()) // TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday Ā· 11:29 PM) return _('Yesterday惻%s').format(_ltdFormat.format(time)); // Less than a day ago if (diff < TIME_SPAN_DAY) return _ltdFormat.format(time); // Less than a week ago if (diff < TIME_SPAN_WEEK) return _ltwLong.format(time); // Sometime this year if (date.getFullYear() === now.getFullYear()) return _ltyLong.format(time); // Earlier than that return _gtyLong.format(time); } /** * Return a human-readable timestamp, formatted for shorter contexts. * * @param {number} time - Milliseconds since the epoch (local time) * @return {string} A localized timestamp similar to what Android Messages uses */ function getShortTime(time) { const date = new Date(time); const now = new Date(); const diff = now - time; if (diff < TIME_SPAN_MINUTE) // TRANSLATORS: Less than a minute ago return _('Just now'); if (diff < TIME_SPAN_HOUR) { // TRANSLATORS: Time duration in minutes (eg. 15 minutes) return ngettext( '%d minute', '%d minutes', (diff / TIME_SPAN_MINUTE) ).format(diff / TIME_SPAN_MINUTE); } // Less than a day ago if (diff < TIME_SPAN_DAY) return _ltdFormat.format(time); // Less than a week ago if (diff < TIME_SPAN_WEEK) return _ltwShort.format(time); // Sometime this year if (date.getFullYear() === now.getFullYear()) return _ltyShort.format(time); // Earlier than that return _gtyShort.format(time); } /** * Return a human-readable timestamp, similar to `strftime()` with `%c`. * * @param {number} time - Milliseconds since the epoch (local time) * @return {string} A localized timestamp */ function getDetailedTime(time) { return _cFormat.format(time); } function setAvatarVisible(row, visible) { const incoming = row.message.type === Sms.MessageBox.INBOX; // Adjust the margins if (visible) { row.grid.margin_start = incoming ? 6 : 56; row.grid.margin_bottom = 6; } else { row.grid.margin_start = incoming ? 44 : 56; row.grid.margin_bottom = 0; } // Show hide the avatar if (incoming) row.avatar.visible = visible; } /** * A ListBoxRow for a preview of a conversation */ const ConversationMessage = GObject.registerClass({ GTypeName: 'GSConnectMessagingConversationMessage', Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/messaging-conversation-message.ui', Children: ['grid', 'avatar', 'sender-label', 'message-label'], }, class ConversationMessage extends Gtk.ListBoxRow { _init(contact, message) { super._init(); this.contact = contact; this.message = message; // Sort properties this.sender = message.addresses[0].address || 'unknown'; this.message_label.label = URI.linkify(message.body); this.message_label.tooltip_text = getDetailedTime(message.date); // Add avatar for incoming messages if (message.type === Sms.MessageBox.INBOX) { this.grid.margin_end = 18; this.grid.halign = Gtk.Align.START; this.avatar.contact = this.contact; this.avatar.visible = true; this.sender_label.label = contact.name; this.sender_label.visible = true; this.message_label.get_style_context().add_class('message-in'); this.message_label.halign = Gtk.Align.START; } else { this.message_label.get_style_context().add_class('message-out'); } } _onActivateLink(label, uri) { Gtk.show_uri_on_window( this.get_toplevel(), uri.includes('://') ? uri : `https://${uri}`, Gtk.get_current_event_time() ); return true; } get date() { return this._message.date; } get thread_id() { return this._message.thread_id; } get message() { if (this._message === undefined) this._message = null; return this._message; } set message(message) { this._message = message; } }); /** * A widget for displaying a conversation thread, with an entry for responding. */ const Conversation = GObject.registerClass({ GTypeName: 'GSConnectMessagingConversation', Properties: { 'device': GObject.ParamSpec.object( 'device', 'Device', 'The device associated with this conversation', GObject.ParamFlags.READWRITE, GObject.Object ), 'plugin': GObject.ParamSpec.object( 'plugin', 'Plugin', 'The plugin providing this conversation', GObject.ParamFlags.READWRITE, GObject.Object ), 'has-pending': GObject.ParamSpec.boolean( 'has-pending', 'Has Pending', 'Whether there are sent messages pending confirmation', GObject.ParamFlags.READABLE, false ), 'thread-id': GObject.ParamSpec.string( 'thread-id', 'Thread ID', 'The current thread', GObject.ParamFlags.READWRITE, '' ), }, Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/messaging-conversation.ui', Children: [ 'entry', 'list', 'scrolled', 'pending', 'pending-box', ], }, class MessagingConversation extends Gtk.Grid { _init(params) { super._init({ device: params.device, plugin: params.plugin, }); Object.assign(this, params); this.device.bind_property( 'connected', this.entry, 'sensitive', GObject.BindingFlags.SYNC_CREATE ); // If we're disconnected pending messages might not succeed, but we'll // leave them until reconnect when we'll ask for an update this._connectedId = this.device.connect( 'notify::connected', this._onConnected.bind(this) ); // Pending messages this.pending.message = { date: Number.MAX_SAFE_INTEGER, type: Sms.MessageBox.OUTBOX, }; // Auto-scrolling this._vadj = this.scrolled.get_vadjustment(); this._scrolledId = this._vadj.connect( 'value-changed', this._holdPosition.bind(this) ); // Message List this.list.set_header_func(this._headerMessages); this.list.set_sort_func(this._sortMessages); this._populateMessages(); // Cleanup on ::destroy this.connect('destroy', this._onDestroy); } get addresses() { if (this._addresses === undefined) this._addresses = []; return this._addresses; } set addresses(addresses) { if (!addresses || addresses.length === 0) { this._addresses = []; this._contacts = {}; return; } // Lookup a contact for each address object, then loop back to correct // each address carried by the message. this._addresses = addresses; for (let i = 0, len = this.addresses.length; i < len; i++) { // Lookup the contact const address = this.addresses[i].address; const contact = this.device.contacts.query({number: address}); // Get corrected address let number = address.toPhoneNumber(); if (!number) continue; for (const contactNumber of contact.numbers) { const cnumber = contactNumber.value.toPhoneNumber(); if (cnumber && (number.endsWith(cnumber) || cnumber.endsWith(number))) { number = contactNumber.value; break; } } // Store the final result this.addresses[i].address = number; this.contacts[address] = contact; } // TODO: Mark the entry as insensitive for group messages if (this.addresses.length > 1) { this.entry.placeholder_text = _('Not available'); this.entry.secondary_icon_name = null; this.entry.secondary_icon_tooltip_text = null; this.entry.sensitive = false; this.entry.tooltip_text = null; } } get contacts() { if (this._contacts === undefined) this._contacts = {}; return this._contacts; } get has_pending() { if (this.pending_box === undefined) return false; return (this.pending_box.get_children().length > 0); } get plugin() { if (this._plugin === undefined) this._plugin = null; return this._plugin; } set plugin(plugin) { this._plugin = plugin; } get thread_id() { if (this._thread_id === undefined) this._thread_id = null; return this._thread_id; } set thread_id(thread_id) { const thread = this.plugin.threads[thread_id]; const message = (thread) ? thread[0] : null; if (message && this.addresses.length === 0) { this.addresses = message.addresses; this._thread_id = thread_id; } } _onConnected(device) { if (device.connected) this.pending_box.foreach(msg => msg.destroy()); } _onDestroy(conversation) { conversation.device.disconnect(conversation._connectedId); conversation._vadj.disconnect(conversation._scrolledId); conversation.list.foreach(message => { // HACK: temporary mitigator for mysterious GtkListBox leak message.destroy(); imports.system.gc(); }); } _onEdgeReached(scrolled_window, pos) { // Try to load more messages if (pos === Gtk.PositionType.TOP) this.logPrevious(); // Release any hold to resume auto-scrolling else if (pos === Gtk.PositionType.BOTTOM) this._releasePosition(); } _onEntryChanged(entry) { entry.secondary_icon_sensitive = (entry.text.length); } _onKeyPressEvent(entry, event) { const keyval = event.get_keyval()[1]; const state = event.get_state()[1]; const mask = state & Gtk.accelerator_get_default_mod_mask(); if (keyval === Gdk.KEY_Return && (mask & Gdk.ModifierType.SHIFT_MASK)) { entry.emit('insert-at-cursor', '\n'); return true; } return false; } _onSendMessage(entry, signal_id, event) { // Don't send empty texts if (!this.entry.text.trim()) return; // Send the message this.plugin.sendMessage(this.addresses, this.entry.text); // Add a phony message in the pending box const message = new Gtk.Label({ label: URI.linkify(this.entry.text), halign: Gtk.Align.END, selectable: true, use_markup: true, visible: true, wrap: true, wrap_mode: Pango.WrapMode.WORD_CHAR, xalign: 0, }); message.get_style_context().add_class('message-out'); message.date = Date.now(); message.type = Sms.MessageBox.SENT; // Notify to reveal the pending box this.pending_box.add(message); this.notify('has-pending'); // Clear the entry this.entry.text = ''; } _onSizeAllocate(listbox, allocation) { const upper = this._vadj.get_upper(); const pageSize = this._vadj.get_page_size(); // If the scrolled window hasn't been filled yet, load another message if (upper <= pageSize) { this.logPrevious(); this.scrolled.get_child().check_resize(); // We've been asked to hold the position, so we'll reset the adjustment // value and update the hold position } else if (this.__pos) { this._vadj.set_value(upper - this.__pos); // Otherwise we probably appended a message and should scroll to it } else { this._scrollPosition(Gtk.PositionType.BOTTOM); } } /** * Create a message row, ensuring a contact object has been retrieved or * generated for the message. * * @param {Object} message - A dictionary of message data * @return {ConversationMessage} A message row */ _createMessageRow(message) { // Ensure we have a contact const sender = message.addresses[0].address || 'unknown'; if (this.contacts[sender] === undefined) { this.contacts[sender] = this.device.contacts.query({ number: sender, }); } return new ConversationMessage(this.contacts[sender], message); } _populateMessages() { this.__first = null; this.__last = null; this.__pos = 0; this.__messages = []; // Try and find a thread_id for this number if (this.thread_id === null && this.addresses.length) this._thread_id = this.plugin.getThreadIdForAddresses(this.addresses); // Make a copy of the thread and fill the window with messages if (this.plugin.threads[this.thread_id]) { this.__messages = this.plugin.threads[this.thread_id].slice(0); this.logPrevious(); } } _headerMessages(row, before) { // Skip pending if (row.get_name() === 'pending') return; if (before === null) return setAvatarVisible(row, true); // Add date header if the last message was more than an hour ago let header = row.get_header(); if ((row.message.date - before.message.date) > TIME_SPAN_HOUR) { if (!header) { header = new Gtk.Label({visible: true, selectable: true}); header.get_style_context().add_class('dim-label'); row.set_header(header); } header.label = getTime(row.message.date); // Also show the avatar setAvatarVisible(row, true); row.sender_label.visible = row.message.addresses.length > 1; // Or if the previous sender was the same, hide its avatar } else if (row.message.type === before.message.type && row.sender.equalsPhoneNumber(before.sender)) { setAvatarVisible(before, false); setAvatarVisible(row, true); row.sender_label.visible = false; // otherwise show the avatar } else { setAvatarVisible(row, true); } } _holdPosition() { this.__pos = this._vadj.get_upper() - this._vadj.get_value(); } _releasePosition() { this.__pos = 0; } _scrollPosition(pos = Gtk.PositionType.BOTTOM, animate = true) { let vpos = pos; this._vadj.freeze_notify(); if (pos === Gtk.PositionType.BOTTOM) vpos = this._vadj.get_upper() - this._vadj.get_page_size(); if (animate) { Tweener.addTween(this._vadj, { value: vpos, time: 0.5, transition: 'easeInOutCubic', onComplete: () => this._vadj.thaw_notify(), }); } else { GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { this._vadj.set_value(vpos); this._vadj.thaw_notify(); }); } } _sortMessages(row1, row2) { return (row1.message.date > row2.message.date) ? 1 : -1; } /** * Log the next message in the conversation. * * @param {Object} message - A message object */ logNext(message) { try { // TODO: Unsupported MessageBox if (message.type !== Sms.MessageBox.INBOX && message.type !== Sms.MessageBox.SENT) throw TypeError(`invalid message box ${message.type}`); // Append the message const row = this._createMessageRow(message); this.list.add(row); this.list.invalidate_headers(); // Remove the first pending message if (this.has_pending && message.type === Sms.MessageBox.SENT) { this.pending_box.get_children()[0].destroy(); this.notify('has-pending'); } } catch (e) { debug(e); } } /** * Log the previous message in the thread */ logPrevious() { try { const message = this.__messages.pop(); if (!message) return; // TODO: Unsupported MessageBox if (message.type !== Sms.MessageBox.INBOX && message.type !== Sms.MessageBox.SENT) throw TypeError(`invalid message box ${message.type}`); // Prepend the message const row = this._createMessageRow(message); this.list.prepend(row); this.list.invalidate_headers(); } catch (e) { debug(e); } } /** * Set the contents of the message entry * * @param {string} text - The message to place in the entry */ setMessage(text) { this.entry.text = text; this.entry.emit('move-cursor', 0, text.length, false); } }); /** * A ListBoxRow for a preview of a conversation */ const ConversationSummary = GObject.registerClass({ GTypeName: 'GSConnectMessagingConversationSummary', Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/messaging-conversation-summary.ui', Children: ['avatar', 'name-label', 'time-label', 'body-label'], }, class ConversationSummary extends Gtk.ListBoxRow { _init(contacts, message) { super._init(); this.contacts = contacts; this.message = message; } get date() { return this._message.date; } get thread_id() { return this._message.thread_id; } get message() { return this._message; } set message(message) { this._message = message; this._sender = message.addresses[0].address || 'unknown'; // Contact Name let nameLabel = _('Unknown Contact'); // Update avatar for single-recipient messages if (message.addresses.length === 1) { this.avatar.contact = this.contacts[this._sender]; nameLabel = GLib.markup_escape_text(this.avatar.contact.name, -1); } else { this.avatar.contact = null; nameLabel = _('Group Message'); const participants = []; message.addresses.forEach((address) => { participants.push(this.contacts[address.address].name); }); this.name_label.tooltip_text = participants.join(', '); } // Contact Name & Message body let bodyLabel = message.body.split(/\r|\n/)[0]; bodyLabel = GLib.markup_escape_text(bodyLabel, -1); // Ignore the 'read' flag if it's an outgoing message if (message.type === Sms.MessageBox.SENT) { // TRANSLATORS: An outgoing message body in a conversation summary bodyLabel = _('You: %s').format(bodyLabel); // Otherwise make it bold if it's unread } else if (message.read === Sms.MessageStatus.UNREAD) { nameLabel = `<b>${nameLabel}</b>`; bodyLabel = `<b>${bodyLabel}</b>`; } // Set the labels, body always smaller this.name_label.label = nameLabel; this.body_label.label = `<small>${bodyLabel}</small>`; // Time const timeLabel = `<small>${getShortTime(message.date)}</small>`; this.time_label.label = timeLabel; } /** * Update the relative time label. */ update() { const timeLabel = `<small>${getShortTime(this.message.date)}</small>`; this.time_label.label = timeLabel; } }); /** * A Gtk.ApplicationWindow for SMS conversations */ var Window = GObject.registerClass({ GTypeName: 'GSConnectMessagingWindow', Properties: { 'device': GObject.ParamSpec.object( 'device', 'Device', 'The device associated with this window', GObject.ParamFlags.READWRITE, GObject.Object ), 'plugin': GObject.ParamSpec.object( 'plugin', 'Plugin', 'The plugin providing messages', GObject.ParamFlags.READWRITE, GObject.Object ), 'thread-id': GObject.ParamSpec.string( 'thread-id', 'Thread ID', 'The current thread', GObject.ParamFlags.READWRITE, '' ), }, Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/messaging-window.ui', Children: [ 'headerbar', 'infobar', 'thread-list', 'stack', ], }, class MessagingWindow extends Gtk.ApplicationWindow { _init(params) { super._init(params); this.headerbar.subtitle = this.device.name; this.insert_action_group('device', this.device); // Device Status this.device.bind_property( 'connected', this.infobar, 'reveal-child', GObject.BindingFlags.INVERT_BOOLEAN ); // Contacts this.contact_chooser = new Contacts.ContactChooser({ device: this.device, }); this.stack.add_named(this.contact_chooser, 'contact-chooser'); this._numberSelectedId = this.contact_chooser.connect( 'number-selected', this._onNumberSelected.bind(this) ); // Threads this.thread_list.set_sort_func(this._sortThreads); this._threadsChangedId = this.plugin.connect( 'notify::threads', this._onThreadsChanged.bind(this) ); this._timestampThreadsId = GLib.timeout_add_seconds( GLib.PRIORITY_DEFAULT_IDLE, 60, this._timestampThreads.bind(this) ); this._sync(); this._onThreadsChanged(); this.restoreGeometry('messaging'); } vfunc_delete_event(event) { this.saveGeometry(); GLib.source_remove(this._timestampThreadsId); this.contact_chooser.disconnect(this._numberSelectedId); this.plugin.disconnect(this._threadsChangedId); return false; } get plugin() { return this._plugin || null; } set plugin(plugin) { this._plugin = plugin; } get thread_id() { return this.stack.visible_child_name; } set thread_id(thread_id) { thread_id = `${thread_id}`; // FIXME // Reset to the empty placeholder if (!thread_id) { this.thread_list.select_row(null); this.stack.set_visible_child_name('placeholder'); return; } // Create a conversation widget if there isn't one let conversation = this.stack.get_child_by_name(thread_id); const thread = this.plugin.threads[thread_id]; if (conversation === null) { if (!thread) { debug(`Thread ID ${thread_id} not found`); return; } conversation = new Conversation({ device: this.device, plugin: this.plugin, thread_id: thread_id, }); this.stack.add_named(conversation, thread_id); } // Figure out whether this is a multi-recipient thread this._setHeaderBar(thread[0].addresses); // Select the conversation and entry active this.stack.visible_child = conversation; this.stack.visible_child.entry.has_focus = true; // There was a pending message waiting for a conversation to be chosen if (this._pendingShare) { conversation.setMessage(this._pendingShare); this._pendingShare = null; } this._thread_id = thread_id; this.notify('thread_id'); } _setHeaderBar(addresses = []) { const address = addresses[0].address; const contact = this.device.contacts.query({number: address}); if (addresses.length === 1) { this.headerbar.title = contact.name; this.headerbar.subtitle = Contacts.getDisplayNumber(contact, address); } else { const otherLength = addresses.length - 1; this.headerbar.title = contact.name; this.headerbar.subtitle = ngettext( 'And %d other contact', 'And %d others', otherLength ).format(otherLength); } } _sync() { this.device.contacts.fetch(); this.plugin.connected(); } _onNewConversation() { this._sync(); this.stack.set_visible_child_name('contact-chooser'); this.thread_list.select_row(null); this.contact_chooser.entry.has_focus = true; } _onNumberSelected(chooser, number) { const contacts = chooser.getSelected(); const row = this._getRowForContacts(contacts); if (row) row.emit('activate'); else this.setContacts(contacts); } /** * Threads */ _onThreadsChanged() { // Get the last message in each thread const messages = {}; for (const [thread_id, thread] of Object.entries(this.plugin.threads)) { const message = thread[thread.length - 1]; // Skip messages without a body (eg. MMS messages without text) if (message.body) messages[thread_id] = thread[thread.length - 1]; } // Update existing summaries and destroy old ones for (const row of this.thread_list.get_children()) { const message = messages[row.thread_id]; // If it's an existing conversation, update it if (message) { // Ensure there's a contact mapping const sender = message.addresses[0].address || 'unknown'; if (row.contacts[sender] === undefined) { row.contacts[sender] = this.device.contacts.query({ number: sender, }); } row.message = message; delete messages[row.thread_id]; // Otherwise destroy it } else { // Destroy the conversation widget const conversation = this.stack.get_child_by_name(`${row.thread_id}`); if (conversation) { conversation.destroy(); imports.system.gc(); } // Then the summary widget row.destroy(); // HACK: temporary mitigator for mysterious GtkListBox leak imports.system.gc(); } } // What's left in the dictionary is new summaries for (const message of Object.values(messages)) { const contacts = this.device.contacts.lookupAddresses(message.addresses); const conversation = new ConversationSummary(contacts, message); this.thread_list.add(conversation); } // Re-sort the summaries this.thread_list.invalidate_sort(); } // GtkListBox::row-activated _onThreadSelected(box, row) { // Show the conversation for this number (if applicable) if (row) { this.thread_id = row.thread_id; // Show the placeholder } else { this.headerbar.title = _('Messaging'); this.headerbar.subtitle = this.device.name; } } _sortThreads(row1, row2) { return (row1.date > row2.date) ? -1 : 1; } _timestampThreads() { if (this.visible) this.thread_list.foreach(row => row.update()); return GLib.SOURCE_CONTINUE; } /** * Find the thread row for @contacts * * @param {Object[]} contacts - A contact group * @return {ConversationSummary|null} The thread row or %null */ _getRowForContacts(contacts) { const addresses = Object.keys(contacts).map(address => { return {address: address}; }); // Try to find a thread_id const thread_id = this.plugin.getThreadIdForAddresses(addresses); for (const row of this.thread_list.get_children()) { if (row.message.thread_id === thread_id) return row; } return null; } setContacts(contacts) { // Group the addresses const addresses = []; for (const address of Object.keys(contacts)) addresses.push({address: address}); // Try to find a thread ID for this address group let thread_id = this.plugin.getThreadIdForAddresses(addresses); if (thread_id === null) thread_id = GLib.uuid_string_random(); else thread_id = thread_id.toString(); // Try to find a thread row for the ID const row = this._getRowForContacts(contacts); if (row !== null) { this.thread_list.select_row(row); return; } // We're creating a new conversation const conversation = new Conversation({ device: this.device, plugin: this.plugin, addresses: addresses, }); // Set the headerbar this._setHeaderBar(addresses); // Select the conversation and entry active this.stack.add_named(conversation, thread_id); this.stack.visible_child = conversation; this.stack.visible_child.entry.has_focus = true; // There was a pending message waiting for a conversation to be chosen if (this._pendingShare) { conversation.setMessage(this._pendingShare); this._pendingShare = null; } this._thread_id = thread_id; this.notify('thread-id'); } _includesAddress(addresses, addressObj) { const number = addressObj.address.toPhoneNumber(); for (const haystackObj of addresses) { const tnumber = haystackObj.address.toPhoneNumber(); if (number.endsWith(tnumber) || tnumber.endsWith(number)) return true; } return false; } /** * Try and find an existing conversation widget for @message. * * @param {Object} message - A message object * @return {Conversation|null} A conversation widget or %null */ getConversationForMessage(message) { // TODO: This shouldn't happen? if (message === null) return null; // First try to find a conversation by thread_id const thread_id = `${message.thread_id}`; const conversation = this.stack.get_child_by_name(thread_id); if (conversation !== null) return conversation; // Try and find one by matching addresses, which is necessary if we've // started a thread locally and haven't set the thread_id const addresses = message.addresses; for (const conversation of this.stack.get_children()) { if (conversation.addresses === undefined || conversation.addresses.length !== addresses.length) continue; const caddrs = conversation.addresses; // If we find a match, set `thread-id` on the conversation and the // child property `name`. if (addresses.every(addr => this._includesAddress(caddrs, addr))) { conversation._thread_id = thread_id; this.stack.child_set_property(conversation, 'name', thread_id); return conversation; } } return null; } /** * Set the contents of the message entry. If @pending is %false set the * message of the currently selected conversation, otherwise mark the * message to be set for the next selected conversation. * * @param {string} message - The message to place in the entry * @param {boolean} pending - Wait for a conversation to be selected */ setMessage(message, pending = false) { try { if (pending) this._pendingShare = message; else this.stack.visible_child.setMessage(message); } catch (e) { debug(e); } } }); /** * A Gtk.ApplicationWindow for selecting from open conversations */ var ConversationChooser = GObject.registerClass({ GTypeName: 'GSConnectConversationChooser', Properties: { 'device': GObject.ParamSpec.object( 'device', 'Device', 'The device associated with this window', GObject.ParamFlags.READWRITE, GObject.Object ), 'message': GObject.ParamSpec.string( 'message', 'Message', 'The message to share', GObject.ParamFlags.READWRITE, '' ), 'plugin': GObject.ParamSpec.object( 'plugin', 'Plugin', 'The plugin providing messages', GObject.ParamFlags.READWRITE, GObject.Object ), }, }, class ConversationChooser extends Gtk.ApplicationWindow { _init(params) { super._init(Object.assign({ title: _('Share Link'), default_width: 300, default_height: 200, }, params)); this.set_keep_above(true); // HeaderBar this.headerbar = new Gtk.HeaderBar({ title: _('Share Link'), subtitle: this.message, show_close_button: true, tooltip_text: this.message, }); this.set_titlebar(this.headerbar); const newButton = new Gtk.Button({ image: new Gtk.Image({icon_name: 'list-add-symbolic'}), tooltip_text: _('New Conversation'), always_show_image: true, }); newButton.connect('clicked', this._new.bind(this)); this.headerbar.pack_start(newButton); // Threads const scrolledWindow = new Gtk.ScrolledWindow({ can_focus: false, hexpand: true, vexpand: true, hscrollbar_policy: Gtk.PolicyType.NEVER, }); this.add(scrolledWindow); this.thread_list = new Gtk.ListBox({ activate_on_single_click: false, }); this.thread_list.set_sort_func(Window.prototype._sortThreads); this.thread_list.connect('row-activated', this._select.bind(this)); scrolledWindow.add(this.thread_list); // Filter Setup Window.prototype._onThreadsChanged.call(this); this.show_all(); } get plugin() { return this._plugin || null; } set plugin(plugin) { this._plugin = plugin; } _new(button) { const message = this.message; this.destroy(); this.plugin.sms(); this.plugin.window._onNewConversation(); this.plugin.window._pendingShare = message; } _select(box, row) { this.plugin.sms(); this.plugin.window.thread_id = row.message.thread_id.toString(); this.plugin.window.setMessage(this.message); this.destroy(); } }); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/ui/mousepad.js���������������������������������������0000664�0000000�0000000�00000021153�14215434441�0024530�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gdk = imports.gi.Gdk; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; /** * A map of Gdk to "KDE Connect" keyvals */ const ReverseKeyMap = new Map([ [Gdk.KEY_BackSpace, 1], [Gdk.KEY_Tab, 2], [Gdk.KEY_Linefeed, 3], [Gdk.KEY_Left, 4], [Gdk.KEY_Up, 5], [Gdk.KEY_Right, 6], [Gdk.KEY_Down, 7], [Gdk.KEY_Page_Up, 8], [Gdk.KEY_Page_Down, 9], [Gdk.KEY_Home, 10], [Gdk.KEY_End, 11], [Gdk.KEY_Return, 12], [Gdk.KEY_Delete, 13], [Gdk.KEY_Escape, 14], [Gdk.KEY_Sys_Req, 15], [Gdk.KEY_Scroll_Lock, 16], [Gdk.KEY_F1, 21], [Gdk.KEY_F2, 22], [Gdk.KEY_F3, 23], [Gdk.KEY_F4, 24], [Gdk.KEY_F5, 25], [Gdk.KEY_F6, 26], [Gdk.KEY_F7, 27], [Gdk.KEY_F8, 28], [Gdk.KEY_F9, 29], [Gdk.KEY_F10, 30], [Gdk.KEY_F11, 31], [Gdk.KEY_F12, 32], ]); /* * A list of keyvals we consider modifiers */ const MOD_KEYS = [ Gdk.KEY_Alt_L, Gdk.KEY_Alt_R, Gdk.KEY_Caps_Lock, Gdk.KEY_Control_L, Gdk.KEY_Control_R, Gdk.KEY_Meta_L, Gdk.KEY_Meta_R, Gdk.KEY_Num_Lock, Gdk.KEY_Shift_L, Gdk.KEY_Shift_R, Gdk.KEY_Super_L, Gdk.KEY_Super_R, ]; /* * Some convenience functions for checking keyvals for modifiers */ const isAlt = (key) => [Gdk.KEY_Alt_L, Gdk.KEY_Alt_R].includes(key); const isCtrl = (key) => [Gdk.KEY_Control_L, Gdk.KEY_Control_R].includes(key); const isShift = (key) => [Gdk.KEY_Shift_L, Gdk.KEY_Shift_R].includes(key); const isSuper = (key) => [Gdk.KEY_Super_L, Gdk.KEY_Super_R].includes(key); var InputDialog = GObject.registerClass({ GTypeName: 'GSConnectMousepadInputDialog', Properties: { 'device': GObject.ParamSpec.object( 'device', 'Device', 'The device associated with this window', GObject.ParamFlags.READWRITE, GObject.Object ), 'plugin': GObject.ParamSpec.object( 'plugin', 'Plugin', 'The mousepad plugin associated with this window', GObject.ParamFlags.READWRITE, GObject.Object ), }, Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/mousepad-input-dialog.ui', Children: [ 'infobar', 'infobar-label', 'shift-label', 'ctrl-label', 'alt-label', 'super-label', 'entry', ], }, class InputDialog extends Gtk.Dialog { _init(params) { super._init(Object.assign({ use_header_bar: true, }, params)); const headerbar = this.get_titlebar(); headerbar.title = _('Keyboard'); headerbar.subtitle = this.device.name; // Main Box const content = this.get_content_area(); content.border_width = 0; // TRANSLATORS: Displayed when the remote keyboard is not ready to accept input this.infobar_label.label = _('Remote keyboard on %s is not active').format(this.device.name); // Text Input this.entry.buffer.connect( 'insert-text', this._onInsertText.bind(this) ); this.infobar.connect('notify::reveal-child', this._onState.bind(this)); this.plugin.bind_property('state', this.infobar, 'reveal-child', 6); this.show_all(); } vfunc_delete_event(event) { this._ungrab(); return this.hide_on_delete(); } vfunc_grab_broken_event(event) { if (event.keyboard) this._ungrab(); return false; } vfunc_key_release_event(event) { if (!this.plugin.state) debug('ignoring remote keyboard state'); const keyvalLower = Gdk.keyval_to_lower(event.keyval); const realMask = event.state & Gtk.accelerator_get_default_mod_mask(); this.alt_label.sensitive = !isAlt(keyvalLower) && (realMask & Gdk.ModifierType.MOD1_MASK); this.ctrl_label.sensitive = !isCtrl(keyvalLower) && (realMask & Gdk.ModifierType.CONTROL_MASK); this.shift_label.sensitive = !isShift(keyvalLower) && (realMask & Gdk.ModifierType.SHIFT_MASK); this.super_label.sensitive = !isSuper(keyvalLower) && (realMask & Gdk.ModifierType.SUPER_MASK); return super.vfunc_key_release_event(event); } vfunc_key_press_event(event) { if (!this.plugin.state) debug('ignoring remote keyboard state'); let keyvalLower = Gdk.keyval_to_lower(event.keyval); let realMask = event.state & Gtk.accelerator_get_default_mod_mask(); this.alt_label.sensitive = isAlt(keyvalLower) || (realMask & Gdk.ModifierType.MOD1_MASK); this.ctrl_label.sensitive = isCtrl(keyvalLower) || (realMask & Gdk.ModifierType.CONTROL_MASK); this.shift_label.sensitive = isShift(keyvalLower) || (realMask & Gdk.ModifierType.SHIFT_MASK); this.super_label.sensitive = isSuper(keyvalLower) || (realMask & Gdk.ModifierType.SUPER_MASK); // Wait for a real key before sending if (MOD_KEYS.includes(keyvalLower)) return false; // Normalize Tab if (keyvalLower === Gdk.KEY_ISO_Left_Tab) keyvalLower = Gdk.KEY_Tab; // Put shift back if it changed the case of the key, not otherwise. if (keyvalLower !== event.keyval) realMask |= Gdk.ModifierType.SHIFT_MASK; // HACK: we don't want to use SysRq as a keybinding (but we do want // Alt+Print), so we avoid translation from Alt+Print to SysRq if (keyvalLower === Gdk.KEY_Sys_Req && (realMask & Gdk.ModifierType.MOD1_MASK) !== 0) keyvalLower = Gdk.KEY_Print; // CapsLock isn't supported as a keybinding modifier, so keep it from // confusing us realMask &= ~Gdk.ModifierType.LOCK_MASK; if (keyvalLower === 0) return false; debug(`keyval: ${event.keyval}, mask: ${realMask}`); const request = { alt: !!(realMask & Gdk.ModifierType.MOD1_MASK), ctrl: !!(realMask & Gdk.ModifierType.CONTROL_MASK), shift: !!(realMask & Gdk.ModifierType.SHIFT_MASK), super: !!(realMask & Gdk.ModifierType.SUPER_MASK), sendAck: true, }; // specialKey if (ReverseKeyMap.has(event.keyval)) { request.specialKey = ReverseKeyMap.get(event.keyval); // key } else { const codePoint = Gdk.keyval_to_unicode(event.keyval); request.key = String.fromCodePoint(codePoint); } this.device.sendPacket({ type: 'kdeconnect.mousepad.request', body: request, }); // Pass these key combinations rather than using the echo reply if (request.alt || request.ctrl || request.super) return super.vfunc_key_press_event(event); return false; } vfunc_window_state_event(event) { if (!this.plugin.state) debug('ignoring remote keyboard state'); if (event.new_window_state & Gdk.WindowState.FOCUSED) this._grab(); else this._ungrab(); return super.vfunc_window_state_event(event); } _onInsertText(buffer, location, text, len) { if (this._isAck) return; debug(`insert-text: ${text} (chars ${[...text].length})`); for (const char of [...text]) { if (!char) continue; // TODO: modifiers? this.device.sendPacket({ type: 'kdeconnect.mousepad.request', body: { alt: false, ctrl: false, shift: false, super: false, sendAck: false, key: char, }, }); } } _onState(widget) { if (!this.plugin.state) debug('ignoring remote keyboard state'); if (this.is_active) this._grab(); else this._ungrab(); } _grab() { if (!this.visible || this._keyboard) return; const seat = Gdk.Display.get_default().get_default_seat(); const status = seat.grab( this.get_window(), Gdk.SeatCapabilities.KEYBOARD, false, null, null, null ); if (status !== Gdk.GrabStatus.SUCCESS) { logError(new Error('Grabbing keyboard failed')); return; } this._keyboard = seat.get_keyboard(); this.grab_add(); this.entry.has_focus = true; } _ungrab() { if (this._keyboard) { this._keyboard.get_seat().ungrab(); this._keyboard = null; this.grab_remove(); } this.entry.buffer.text = ''; } }); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/ui/notification.js�����������������������������������0000664�0000000�0000000�00000010505�14215434441�0025400�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const URI = imports.service.utils.uri; /** * A dialog for repliable notifications. */ var ReplyDialog = GObject.registerClass({ GTypeName: 'GSConnectNotificationReplyDialog', Properties: { 'device': GObject.ParamSpec.object( 'device', 'Device', 'The device associated with this window', GObject.ParamFlags.READWRITE, GObject.Object ), 'plugin': GObject.ParamSpec.object( 'plugin', 'Plugin', 'The plugin that owns this notification', GObject.ParamFlags.READWRITE, GObject.Object ), 'uuid': GObject.ParamSpec.string( 'uuid', 'UUID', 'The notification reply UUID', GObject.ParamFlags.READWRITE, null ), }, Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/notification-reply-dialog.ui', Children: ['infobar', 'notification-title', 'notification-body', 'entry'], }, class ReplyDialog extends Gtk.Dialog { _init(params) { super._init({ application: Gio.Application.get_default(), device: params.device, plugin: params.plugin, uuid: params.uuid, use_header_bar: true, }); this.set_response_sensitive(Gtk.ResponseType.OK, false); // Info bar this.device.bind_property( 'connected', this.infobar, 'reveal-child', GObject.BindingFlags.INVERT_BOOLEAN ); // Notification Data const headerbar = this.get_titlebar(); headerbar.title = params.notification.appName; headerbar.subtitle = this.device.name; this.notification_title.label = params.notification.title; this.notification_body.label = URI.linkify(params.notification.text); // Message Entry/Send Button this.device.bind_property( 'connected', this.entry, 'sensitive', GObject.BindingFlags.DEFAULT ); this._connectedId = this.device.connect( 'notify::connected', this._onStateChanged.bind(this) ); this._entryChangedId = this.entry.buffer.connect( 'changed', this._onStateChanged.bind(this) ); this.restoreGeometry('notification-reply-dialog'); this.connect('destroy', this._onDestroy); } _onDestroy(dialog) { dialog.entry.buffer.disconnect(dialog._entryChangedId); dialog.device.disconnect(dialog._connectedId); } vfunc_delete_event() { this.saveGeometry(); return false; } vfunc_response(response_id) { if (response_id === Gtk.ResponseType.OK) { // Refuse to send empty or whitespace only messages if (!this.entry.buffer.text.trim()) return; this.plugin.replyNotification( this.uuid, this.entry.buffer.text ); } this.destroy(); } get device() { if (this._device === undefined) this._device = null; return this._device; } set device(device) { this._device = device; } get plugin() { if (this._plugin === undefined) this._plugin = null; return this._plugin; } set plugin(plugin) { this._plugin = plugin; } get uuid() { if (this._uuid === undefined) this._uuid = null; return this._uuid; } set uuid(uuid) { this._uuid = uuid; // We must have a UUID if (!uuid) { this.destroy(); debug('no uuid for repliable notification'); } } _onActivateLink(label, uri) { Gtk.show_uri_on_window( this.get_toplevel(), uri.includes('://') ? uri : `https://${uri}`, Gtk.get_current_event_time() ); return true; } _onStateChanged() { if (this.device.connected && this.entry.buffer.text.trim()) this.set_response_sensitive(Gtk.ResponseType.OK, true); else this.set_response_sensitive(Gtk.ResponseType.OK, false); } }); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/ui/service.js����������������������������������������0000664�0000000�0000000�00000015200�14215434441�0024347�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Config = imports.config; /* * Issue Header */ const ISSUE_HEADER = ` GSConnect: ${Config.PACKAGE_VERSION} (${Config.IS_USER ? 'user' : 'system'}) GJS: ${imports.system.version} Session: ${GLib.getenv('XDG_SESSION_TYPE')} OS: ${GLib.get_os_info('PRETTY_NAME')} `; /** * A dialog for selecting a device */ var DeviceChooser = GObject.registerClass({ GTypeName: 'GSConnectServiceDeviceChooser', Properties: { 'action-name': GObject.ParamSpec.string( 'action-name', 'Action Name', 'The name of the associated action, like "sendFile"', GObject.ParamFlags.READWRITE, null ), 'action-target': GObject.param_spec_variant( 'action-target', 'Action Target', 'The parameter for action invocations', new GLib.VariantType('*'), null, GObject.ParamFlags.READWRITE ), }, Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/service-device-chooser.ui', Children: ['device-list', 'cancel-button', 'select-button'], }, class DeviceChooser extends Gtk.Dialog { _init(params = {}) { super._init({ use_header_bar: true, application: Gio.Application.get_default(), }); this.set_keep_above(true); // HeaderBar this.get_header_bar().subtitle = params.title; // Dialog Action this.action_name = params.action_name; this.action_target = params.action_target; // Device List this.device_list.set_sort_func(this._sortDevices); this._devicesChangedId = this.application.settings.connect( 'changed::devices', this._onDevicesChanged.bind(this) ); this._onDevicesChanged(); } vfunc_response(response_id) { if (response_id === Gtk.ResponseType.OK) { try { const device = this.device_list.get_selected_row().device; device.activate_action(this.action_name, this.action_target); } catch (e) { logError(e); } } this.destroy(); } get action_name() { if (this._action_name === undefined) this._action_name = null; return this._action_name; } set action_name(name) { this._action_name = name; } get action_target() { if (this._action_target === undefined) this._action_target = null; return this._action_target; } set action_target(variant) { this._action_target = variant; } _onDeviceActivated(box, row) { this.response(Gtk.ResponseType.OK); } _onDeviceSelected(box) { this.set_response_sensitive( Gtk.ResponseType.OK, (box.get_selected_row()) ); } _onDevicesChanged() { // Collect known devices const devices = {}; for (const [id, device] of this.application.manager.devices.entries()) devices[id] = device; // Prune device rows this.device_list.foreach(row => { if (!devices.hasOwnProperty(row.name)) row.destroy(); else delete devices[row.name]; }); // Add new devices for (const device of Object.values(devices)) { const action = device.lookup_action(this.action_name); if (action === null) continue; const row = new Gtk.ListBoxRow({ visible: action.enabled, }); row.set_name(device.id); row.device = device; action.bind_property( 'enabled', row, 'visible', Gio.SettingsBindFlags.DEFAULT ); const grid = new Gtk.Grid({ column_spacing: 12, margin: 6, visible: true, }); row.add(grid); const icon = new Gtk.Image({ icon_name: device.icon_name, pixel_size: 32, visible: true, }); grid.attach(icon, 0, 0, 1, 1); const name = new Gtk.Label({ label: device.name, halign: Gtk.Align.START, hexpand: true, visible: true, }); grid.attach(name, 1, 0, 1, 1); this.device_list.add(row); } if (this.device_list.get_selected_row() === null) this.device_list.select_row(this.device_list.get_row_at_index(0)); } _sortDevices(row1, row2) { return row1.device.name.localeCompare(row2.device.name); } }); /** * A dialog for reporting an error. */ var ErrorDialog = GObject.registerClass({ GTypeName: 'GSConnectServiceErrorDialog', Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/service-error-dialog.ui', Children: [ 'error-stack', 'expander-arrow', 'gesture', 'report-button', 'revealer', ], }, class ErrorDialog extends Gtk.Window { _init(error) { super._init({ application: Gio.Application.get_default(), title: `GSConnect: ${error.name}`, }); this.set_keep_above(true); this.error = error; this.error_stack.buffer.text = `${error.message}\n\n${error.stack}`; this.gesture.connect('released', this._onReleased.bind(this)); } _onClicked(button) { if (this.report_button === button) { const uri = this._buildUri(this.error.message, this.error.stack); Gio.AppInfo.launch_default_for_uri_async(uri, null, null, null); } this.destroy(); } _onReleased(gesture, n_press) { if (n_press === 1) this.revealer.reveal_child = !this.revealer.reveal_child; } _onRevealChild(revealer, pspec) { this.expander_arrow.icon_name = this.revealer.reveal_child ? 'pan-down-symbolic' : 'pan-end-symbolic'; } _buildUri(message, stack) { const body = `\`\`\`${ISSUE_HEADER}\n${stack}\n\`\`\``; const titleQuery = encodeURIComponent(message).replace('%20', '+'); const bodyQuery = encodeURIComponent(body).replace('%20', '+'); const uri = `${Config.PACKAGE_BUGREPORT}?title=${titleQuery}&body=${bodyQuery}`; // Reasonable URI length limit if (uri.length > 2000) return uri.substr(0, 2000); return uri; } }); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/utils/�����������������������������������������������0000775�0000000�0000000�00000000000�14215434441�0023076�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/utils/dbus.js����������������������������������������0000664�0000000�0000000�00000020475�14215434441�0024401�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gio = imports.gi.Gio; const GjsPrivate = imports.gi.GjsPrivate; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; /* * Some utility methods */ function toDBusCase(string) { return string.replace(/(?:^\w|[A-Z]|\b\w)/g, (ltr, offset) => { return ltr.toUpperCase(); }).replace(/[\s_-]+/g, ''); } function toHyphenCase(string) { return string.replace(/(?:[A-Z])/g, (ltr, offset) => { return (offset > 0) ? `-${ltr.toLowerCase()}` : ltr.toLowerCase(); }).replace(/[\s_]+/g, ''); } function toUnderscoreCase(string) { return string.replace(/(?:^\w|[A-Z]|_|\b\w)/g, (ltr, offset) => { if (ltr === '_') return ''; return (offset > 0) ? `_${ltr.toLowerCase()}` : ltr.toLowerCase(); }).replace(/[\s-]+/g, ''); } /** * DBus.Interface represents a DBus interface bound to an object instance, meant * to be exported over DBus. */ var Interface = GObject.registerClass({ GTypeName: 'GSConnectDBusInterface', Implements: [Gio.DBusInterface], Properties: { 'g-instance': GObject.ParamSpec.object( 'g-instance', 'Instance', 'The delegate GObject', GObject.ParamFlags.READWRITE, GObject.Object.$gtype ), }, }, class Interface extends GjsPrivate.DBusImplementation { _init(params) { super._init({ g_instance: params.g_instance, g_interface_info: params.g_interface_info, }); // Cache member lookups this._instanceHandlers = []; this._instanceMethods = {}; this._instanceProperties = {}; const info = this.get_info(); this.connect('handle-method-call', this._call.bind(this._instance, info)); this.connect('handle-property-get', this._get.bind(this._instance, info)); this.connect('handle-property-set', this._set.bind(this._instance, info)); // Automatically forward known signals const id = this._instance.connect('notify', this._notify.bind(this)); this._instanceHandlers.push(id); for (const signal of info.signals) { const type = `(${signal.args.map(arg => arg.signature).join('')})`; const id = this._instance.connect( signal.name, this._emit.bind(this, signal.name, type) ); this._instanceHandlers.push(id); } // Export if connection and object path were given if (params.g_connection && params.g_object_path) this.export(params.g_connection, params.g_object_path); } get g_instance() { if (this._instance === undefined) this._instance = null; return this._instance; } set g_instance(instance) { this._instance = instance; } /** * Invoke an instance's method for a DBus method call. * * @param {Gio.DBusInterfaceInfo} info - The DBus interface * @param {DBus.Interface} iface - The DBus interface * @param {string} name - The DBus method name * @param {GLib.Variant} parameters - The method parameters * @param {Gio.DBusMethodInvocation} invocation - The method invocation info */ async _call(info, iface, name, parameters, invocation) { let retval; // Invoke the instance method try { const args = parameters.unpack().map(parameter => { if (parameter.get_type_string() === 'h') { const message = invocation.get_message(); const fds = message.get_unix_fd_list(); const idx = parameter.deepUnpack(); return fds.get(idx); } else { return parameter.recursiveUnpack(); } }); retval = await this[name](...args); } catch (e) { if (e instanceof GLib.Error) { invocation.return_gerror(e); } else { // likely to be a normal JS error if (!e.name.includes('.')) e.name = `org.gnome.gjs.JSError.${e.name}`; invocation.return_dbus_error(e.name, e.message); } logError(e, `${this}: ${name}`); return; } // `undefined` is an empty tuple on DBus if (retval === undefined) retval = new GLib.Variant('()', []); // Return the instance result or error try { if (!(retval instanceof GLib.Variant)) { const args = info.lookup_method(name).out_args; retval = new GLib.Variant( `(${args.map(arg => arg.signature).join('')})`, (args.length === 1) ? [retval] : retval ); } invocation.return_value(retval); } catch (e) { invocation.return_dbus_error( 'org.gnome.gjs.JSError.ValueError', 'Service implementation returned an incorrect value type' ); logError(e, `${this}: ${name}`); } } _nativeProp(obj, name) { if (this._instanceProperties[name] === undefined) { let propName = name; if (propName in obj) this._instanceProperties[name] = propName; if (this._instanceProperties[name] === undefined) { propName = toUnderscoreCase(name); if (propName in obj) this._instanceProperties[name] = propName; } } return this._instanceProperties[name]; } _emit(name, type, obj, ...args) { this.emit_signal(name, new GLib.Variant(type, args)); } _get(info, iface, name) { const nativeValue = this[iface._nativeProp(this, name)]; const propertyInfo = info.lookup_property(name); if (nativeValue === undefined || propertyInfo === null) return null; return new GLib.Variant(propertyInfo.signature, nativeValue); } _set(info, iface, name, value) { const nativeValue = value.recursiveUnpack(); this[iface._nativeProp(this, name)] = nativeValue; } _notify(obj, pspec) { const name = toDBusCase(pspec.name); const propertyInfo = this.get_info().lookup_property(name); if (propertyInfo === null) return; this.emit_property_changed( name, new GLib.Variant( propertyInfo.signature, // Adjust for GJS's '-'/'_' conversion this._instance[pspec.name.replace(/-/gi, '_')] ) ); } destroy() { try { for (const id of this._instanceHandlers) this._instance.disconnect(id); this._instanceHandlers = []; this.flush(); this.unexport(); } catch (e) { logError(e); } } }); /** * Get the DBus connection on @busType * * @param {Gio.BusType} [busType] - a Gio.BusType constant * @param {Gio.Cancellable} [cancellable] - an optional Gio.Cancellable * @return {Promise<Gio.DBusConnection>} A DBus connection */ function getConnection(busType = Gio.BusType.SESSION, cancellable = null) { return new Promise((resolve, reject) => { Gio.bus_get(busType, cancellable, (connection, res) => { try { resolve(Gio.bus_get_finish(res)); } catch (e) { reject(e); } }); }); } /** * Get a new, dedicated DBus connection on @busType * * @param {Gio.BusType} [busType] - a Gio.BusType constant * @param {Gio.Cancellable} [cancellable] - an optional Gio.Cancellable * @return {Promise<Gio.DBusConnection>} A new DBus connection */ function newConnection(busType = Gio.BusType.SESSION, cancellable = null) { return new Promise((resolve, reject) => { Gio.DBusConnection.new_for_address( Gio.dbus_address_get_for_bus_sync(busType, cancellable), Gio.DBusConnectionFlags.AUTHENTICATION_CLIENT | Gio.DBusConnectionFlags.MESSAGE_BUS_CONNECTION, null, cancellable, (connection, res) => { try { resolve(Gio.DBusConnection.new_for_address_finish(res)); } catch (e) { reject(e); } } ); }); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/service/utils/uri.js�����������������������������������������0000664�0000000�0000000�00000012641�14215434441�0024237�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const GLib = imports.gi.GLib; /** * The same regular expression used in GNOME Shell * * http://daringfireball.net/2010/07/improved_regex_for_matching_urls */ const _balancedParens = '\\((?:[^\\s()<>]+|(?:\\(?:[^\\s()<>]+\\)))*\\)'; const _leadingJunk = '[\\s`(\\[{\'\\"<\u00AB\u201C\u2018]'; const _notTrailingJunk = '[^\\s`!()\\[\\]{};:\'\\".,<>?\u00AB\u00BB\u201C\u201D\u2018\u2019]'; const _urlRegexp = new RegExp( '(^|' + _leadingJunk + ')' + '(' + '(?:' + '(?:http|https)://' + // scheme:// '|' + 'www\\d{0,3}[.]' + // www. '|' + '[a-z0-9.\\-]+[.][a-z]{2,4}/' + // foo.xx/ ')' + '(?:' + // one or more: '[^\\s()<>]+' + // run of non-space non-() '|' + // or _balancedParens + // balanced parens ')+' + '(?:' + // end with: _balancedParens + // balanced parens '|' + // or _notTrailingJunk + // last non-junk char ')' + ')', 'gi'); /** * sms/tel URI RegExp (https://tools.ietf.org/html/rfc5724) * * A fairly lenient regexp for sms: URIs that allows tel: numbers with chars * from global-number, local-number (without phone-context) and single spaces. * This allows passing numbers directly from libfolks or GData without * pre-processing. It also makes an allowance for URIs passed from Gio.File * that always come in the form "sms:///". */ const _smsParam = "[\\w.!~*'()-]+=(?:[\\w.!~*'()-]|%[0-9A-F]{2})*"; const _telParam = ";[a-zA-Z0-9-]+=(?:[\\w\\[\\]/:&+$.!~*'()-]|%[0-9A-F]{2})+"; const _lenientDigits = '[+]?(?:[0-9A-F*#().-]| (?! )|%20(?!%20))+'; const _lenientNumber = `${_lenientDigits}(?:${_telParam})*`; const _smsRegex = new RegExp( '^' + 'sms:' + // scheme '(?:[/]{2,3})?' + // Gio.File returns ":///" '(' + // one or more... _lenientNumber + // phone numbers '(?:,' + _lenientNumber + ')*' + // separated by commas ')' + '(?:\\?(' + // followed by optional... _smsParam + // parameters... '(?:&' + _smsParam + ')*' + // separated by "&" (unescaped) '))?' + '$', 'g'); // fragments (#foo) not allowed const _numberRegex = new RegExp( '^' + '(' + _lenientDigits + ')' + // phone number digits '((?:' + _telParam + ')*)' + // followed by optional parameters '$', 'g'); /** * Searches @str for URLs and returns an array of objects with %url * properties showing the matched URL string, and %pos properties indicating * the position within @str where the URL was found. * * @param {string} str - the string to search * @return {Object[]} the list of match objects, as described above */ function findUrls(str) { _urlRegexp.lastIndex = 0; const res = []; let match; while ((match = _urlRegexp.exec(str))) { const name = match[2]; const url = GLib.uri_parse_scheme(name) ? name : `http://${name}`; res.push({name, url, pos: match.index + match[1].length}); } return res; } /** * Return a string with URLs couched in <a> tags, parseable by Pango and * using the same RegExp as GNOME Shell. * * @param {string} str - The string to be modified * @param {string} [title] - An optional title (eg. alt text, tooltip) * @return {string} the modified text */ function linkify(str, title = null) { const text = GLib.markup_escape_text(str, -1); _urlRegexp.lastIndex = 0; if (title) { return text.replace( _urlRegexp, `$1<a href="$2" title="${title}">$2</a>` ); } else { return text.replace(_urlRegexp, '$1<a href="$2">$2</a>'); } } /** * A simple parsing class for sms: URI's (https://tools.ietf.org/html/rfc5724) */ var SmsURI = class URI { constructor(uri) { _smsRegex.lastIndex = 0; const [, recipients, query] = _smsRegex.exec(uri); this.recipients = recipients.split(',').map(recipient => { _numberRegex.lastIndex = 0; const [, number, params] = _numberRegex.exec(recipient); if (params) { for (const param of params.substr(1).split(';')) { const [key, value] = param.split('='); // add phone-context to beginning of if (key === 'phone-context' && value.startsWith('+')) return value + unescape(number); } } return unescape(number); }); if (query) { for (const field of query.split('&')) { const [key, value] = field.split('='); if (key === 'body') { if (this.body) throw URIError('duplicate "body" field'); this.body = value ? decodeURIComponent(value) : undefined; } } } } toString() { const uri = `sms:${this.recipients.join(',')}`; return this.body ? `${uri}?body=${escape(this.body)}` : uri; } }; �����������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/shell/�������������������������������������������������������0000775�0000000�0000000�00000000000�14215434441�0021405�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/shell/__init__.js��������������������������������������������0000664�0000000�0000000�00000002137�14215434441�0023505�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gettext = imports.gettext; const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const Gtk = imports.gi.Gtk; const Extension = imports.misc.extensionUtils.getCurrentExtension(); const Config = Extension.imports.config; Config.PACKAGE_DATADIR = Extension.path; // Ensure config.js is setup properly const userDir = GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell']); if (Config.PACKAGE_DATADIR.startsWith(userDir)) { Config.IS_USER = true; Config.GSETTINGS_SCHEMA_DIR = `${Extension.path}/schemas`; Config.PACKAGE_LOCALEDIR = `${Extension.path}/locale`; } // Init Gettext Gettext.bindtextdomain(Config.APP_ID, Config.PACKAGE_LOCALEDIR); Extension._ = GLib.dgettext.bind(null, Config.APP_ID); Extension.ngettext = GLib.dngettext.bind(null, Config.APP_ID); // Init GResources Gio.Resource.load( GLib.build_filenamev([Config.PACKAGE_DATADIR, `${Config.APP_ID}.gresource`]) )._register(); // Init GSchema Config.GSCHEMA = Gio.SettingsSchemaSource.new_from_directory( Config.GSETTINGS_SCHEMA_DIR, Gio.SettingsSchemaSource.get_default(), false ); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/shell/clipboard.js�������������������������������������������0000664�0000000�0000000�00000024713�14215434441�0023711�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const ByteArray = imports.byteArray; const Gio = imports.gi.Gio; const GjsPrivate = imports.gi.GjsPrivate; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Meta = imports.gi.Meta; /* * DBus Interface Info */ const DBUS_NAME = 'org.gnome.Shell.Extensions.GSConnect.Clipboard'; const DBUS_PATH = '/org/gnome/Shell/Extensions/GSConnect/Clipboard'; const DBUS_NODE = Gio.DBusNodeInfo.new_for_xml(` <node> <interface name="org.gnome.Shell.Extensions.GSConnect.Clipboard"> <!-- Methods --> <method name="GetMimetypes"> <arg direction="out" type="as" name="mimetypes"/> </method> <method name="GetText"> <arg direction="out" type="s" name="text"/> </method> <method name="SetText"> <arg direction="in" type="s" name="text"/> </method> <method name="GetValue"> <arg direction="in" type="s" name="mimetype"/> <arg direction="out" type="ay" name="value"/> </method> <method name="SetValue"> <arg direction="in" type="ay" name="value"/> <arg direction="in" type="s" name="mimetype"/> </method> <!-- Signals --> <signal name="OwnerChange"/> </interface> </node> `); const DBUS_INFO = DBUS_NODE.lookup_interface(DBUS_NAME); /* * Text Mimetypes */ const TEXT_MIMETYPES = [ 'text/plain;charset=utf-8', 'UTF8_STRING', 'text/plain', 'STRING', ]; /* GSConnectClipboardPortal: * * A simple clipboard portal, especially useful on Wayland where GtkClipboard * doesn't work in the background. */ var Clipboard = GObject.registerClass({ GTypeName: 'GSConnectShellClipboard', }, class GSConnectShellClipboard extends GjsPrivate.DBusImplementation { _init(params = {}) { super._init({ g_interface_info: DBUS_INFO, }); this._transferring = false; // Watch global selection this._selection = global.display.get_selection(); this._ownerChangedId = this._selection.connect( 'owner-changed', this._onOwnerChanged.bind(this) ); // Prepare DBus interface this._handleMethodCallId = this.connect( 'handle-method-call', this._onHandleMethodCall.bind(this) ); this._nameId = Gio.DBus.own_name( Gio.BusType.SESSION, DBUS_NAME, Gio.BusNameOwnerFlags.NONE, this._onBusAcquired.bind(this), null, this._onNameLost.bind(this) ); } _onOwnerChanged(selection, type, source) { /* We're only interested in the standard clipboard */ if (type !== Meta.SelectionType.SELECTION_CLIPBOARD) return; /* In Wayland an intermediate GMemoryOutputStream is used which triggers * a second ::owner-changed emission, so we need to ensure we ignore * that while the transfer is resolving. */ if (this._transferring) return; this._transferring = true; /* We need to put our signal emission in an idle callback to ensure that * Mutter's internal calls have finished resolving in the loop, or else * we'll end up with the previous selection's content. */ GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { this.emit_signal('OwnerChange', null); this._transferring = false; return GLib.SOURCE_REMOVE; }); } _onBusAcquired(connection, name) { try { this.export(connection, DBUS_PATH); } catch (e) { logError(e); } } _onNameLost(connection, name) { try { this.unexport(); } catch (e) { logError(e); } } async _onHandleMethodCall(iface, name, parameters, invocation) { let retval; try { const args = parameters.recursiveUnpack(); retval = await this[name](...args); } catch (e) { if (e instanceof GLib.Error) { invocation.return_gerror(e); } else { if (!e.name.includes('.')) e.name = `org.gnome.gjs.JSError.${e.name}`; invocation.return_dbus_error(e.name, e.message); } return; } if (retval === undefined) retval = new GLib.Variant('()', []); try { if (!(retval instanceof GLib.Variant)) { const args = DBUS_INFO.lookup_method(name).out_args; retval = new GLib.Variant( `(${args.map(arg => arg.signature).join('')})`, (args.length === 1) ? [retval] : retval ); } invocation.return_value(retval); // Without a response, the client will wait for timeout } catch (e) { invocation.return_dbus_error( 'org.gnome.gjs.JSError.ValueError', 'Service implementation returned an incorrect value type' ); } } /** * Get the available mimetypes of the current clipboard content * * @return {Promise<string[]>} A list of mime-types */ GetMimetypes() { return new Promise((resolve, reject) => { try { const mimetypes = this._selection.get_mimetypes( Meta.SelectionType.SELECTION_CLIPBOARD ); resolve(mimetypes); } catch (e) { reject(e); } }); } /** * Get the text content of the clipboard * * @return {Promise<string>} Text content of the clipboard */ GetText() { return new Promise((resolve, reject) => { const mimetypes = this._selection.get_mimetypes( Meta.SelectionType.SELECTION_CLIPBOARD); const mimetype = TEXT_MIMETYPES.find(type => mimetypes.includes(type)); if (mimetype !== undefined) { const stream = Gio.MemoryOutputStream.new_resizable(); this._selection.transfer_async( Meta.SelectionType.SELECTION_CLIPBOARD, mimetype, -1, stream, null, (selection, res) => { try { selection.transfer_finish(res); const bytes = stream.steal_as_bytes(); const bytearray = bytes.get_data(); resolve(ByteArray.toString(bytearray)); } catch (e) { reject(e); } } ); } else { reject(new Error('text not available')); } }); } /** * Set the text content of the clipboard * * @param {string} text - text content to set * @return {Promise} A promise for the operation */ SetText(text) { return new Promise((resolve, reject) => { try { if (typeof text !== 'string') { throw new Gio.DBusError({ code: Gio.DBusError.INVALID_ARGS, message: 'expected string', }); } const source = Meta.SelectionSourceMemory.new( 'text/plain;charset=utf-8', GLib.Bytes.new(text)); this._selection.set_owner( Meta.SelectionType.SELECTION_CLIPBOARD, source); resolve(); } catch (e) { reject(e); } }); } /** * Get the content of the clipboard with the type @mimetype. * * @param {string} mimetype - the mimetype to request * @return {Promise<Uint8Array>} The content of the clipboard */ GetValue(mimetype) { return new Promise((resolve, reject) => { const stream = Gio.MemoryOutputStream.new_resizable(); this._selection.transfer_async( Meta.SelectionType.SELECTION_CLIPBOARD, mimetype, -1, stream, null, (selection, res) => { try { selection.transfer_finish(res); const bytes = stream.steal_as_bytes(); resolve(bytes.get_data()); } catch (e) { reject(e); } } ); }); } /** * Set the content of the clipboard to @value with the type @mimetype. * * @param {Uint8Array} value - the value to set * @param {string} mimetype - the mimetype of the value * @return {Promise} - A promise for the operation */ SetValue(value, mimetype) { return new Promise((resolve, reject) => { try { const source = Meta.SelectionSourceMemory.new(mimetype, GLib.Bytes.new(value)); this._selection.set_owner( Meta.SelectionType.SELECTION_CLIPBOARD, source); resolve(); } catch (e) { reject(e); } }); } destroy() { if (this._selection && this._ownerChangedId > 0) { this._selection.disconnect(this._ownerChangedId); this._ownerChangedId = 0; } if (this._nameId > 0) { Gio.bus_unown_name(this._nameId); this._nameId = 0; } if (this._handleMethodCallId > 0) { this.disconnect(this._handleMethodCallId); this._handleMethodCallId = 0; this.unexport(); } } }); var _portal = null; var _portalId = 0; /** * Watch for the service to start and export the clipboard portal when it does. */ function watchService() { if (GLib.getenv('XDG_SESSION_TYPE') !== 'wayland') return; if (_portalId > 0) return; _portalId = Gio.bus_watch_name( Gio.BusType.SESSION, 'org.gnome.Shell.Extensions.GSConnect', Gio.BusNameWatcherFlags.NONE, () => { if (_portal === null) _portal = new Clipboard(); }, () => { if (_portal !== null) { _portal.destroy(); _portal = null; } } ); } /** * Stop watching the service and export the portal if currently running. */ function unwatchService() { if (_portalId > 0) { Gio.bus_unwatch_name(_portalId); _portalId = 0; } } �����������������������������������������������������gnome-shell-extension-gsconnect-50/src/shell/device.js����������������������������������������������0000664�0000000�0000000�00000025566�14215434441�0023220�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Clutter = imports.gi.Clutter; const GObject = imports.gi.GObject; const St = imports.gi.St; const PanelMenu = imports.ui.panelMenu; const PopupMenu = imports.ui.popupMenu; const Extension = imports.misc.extensionUtils.getCurrentExtension(); // eslint-disable-next-line no-redeclare const _ = Extension._; const GMenu = Extension.imports.shell.gmenu; const Tooltip = Extension.imports.shell.tooltip; /** * A battery widget with an icon, text percentage and time estimate tooltip */ var Battery = GObject.registerClass({ GTypeName: 'GSConnectShellDeviceBattery', }, class Battery extends St.BoxLayout { _init(params) { super._init({ reactive: true, style_class: 'gsconnect-device-battery', track_hover: true, }); Object.assign(this, params); // Percent Label this.label = new St.Label({ y_align: Clutter.ActorAlign.CENTER, }); this.label.clutter_text.ellipsize = 0; this.add_child(this.label); // Battery Icon this.icon = new St.Icon({ fallback_icon_name: 'battery-missing-symbolic', icon_size: 16, }); this.add_child(this.icon); // Battery Estimate this.tooltip = new Tooltip.Tooltip({ parent: this, text: null, }); // Battery GAction this._actionAddedId = this.device.action_group.connect( 'action-added', this._onActionChanged.bind(this) ); this._actionRemovedId = this.device.action_group.connect( 'action-removed', this._onActionChanged.bind(this) ); this._actionStateChangedId = this.device.action_group.connect( 'action-state-changed', this._onStateChanged.bind(this) ); this._onActionChanged(this.device.action_group, 'battery'); // Cleanup on destroy this.connect('destroy', this._onDestroy); } _onActionChanged(action_group, action_name) { if (action_name !== 'battery') return; if (action_group.has_action('battery')) { const value = action_group.get_action_state('battery'); const [charging, icon_name, level, time] = value.deepUnpack(); this._state = { charging: charging, icon_name: icon_name, level: level, time: time, }; } else { this._state = null; } this._sync(); } _onStateChanged(action_group, action_name, value) { if (action_name !== 'battery') return; const [charging, icon_name, level, time] = value.deepUnpack(); this._state = { charging: charging, icon_name: icon_name, level: level, time: time, }; this._sync(); } _getBatteryLabel() { if (!this._state) return null; const {charging, level, time} = this._state; if (level === 100) // TRANSLATORS: When the battery level is 100% return _('Fully Charged'); if (time === 0) // TRANSLATORS: When no time estimate for the battery is available // EXAMPLE: 42% (Estimating…) return _('%d%% (Estimating…)').format(level); const total = time / 60; const minutes = Math.floor(total % 60); const hours = Math.floor(total / 60); if (charging) { // TRANSLATORS: Estimated time until battery is charged // EXAMPLE: 42% (1:15 Until Full) return _('%d%% (%d\u2236%02d Until Full)').format( level, hours, minutes ); } else { // TRANSLATORS: Estimated time until battery is empty // EXAMPLE: 42% (12:15 Remaining) return _('%d%% (%d\u2236%02d Remaining)').format( level, hours, minutes ); } } _onDestroy(actor) { actor.device.action_group.disconnect(actor._actionAddedId); actor.device.action_group.disconnect(actor._actionRemovedId); actor.device.action_group.disconnect(actor._actionStateChangedId); } _sync() { this.visible = !!this._state; if (!this.visible) return; this.icon.icon_name = this._state.icon_name; this.label.text = (this._state.level > -1) ? `${this._state.level}%` : ''; this.tooltip.text = this._getBatteryLabel(); } }); /** * A cell signal strength widget with two icons */ var SignalStrength = GObject.registerClass({ GTypeName: 'GSConnectShellDeviceSignalStrength', }, class SignalStrength extends St.BoxLayout { _init(params) { super._init({ reactive: true, style_class: 'gsconnect-device-signal-strength', track_hover: true, }); Object.assign(this, params); // Network Type Icon this.networkTypeIcon = new St.Icon({ fallback_icon_name: 'network-cellular-symbolic', icon_size: 16, }); this.add_child(this.networkTypeIcon); // Signal Strength Icon this.signalStrengthIcon = new St.Icon({ fallback_icon_name: 'network-cellular-offline-symbolic', icon_size: 16, }); this.add_child(this.signalStrengthIcon); // Network Type Text this.tooltip = new Tooltip.Tooltip({ parent: this, text: null, }); // ConnectivityReport GAction this._actionAddedId = this.device.action_group.connect( 'action-added', this._onActionChanged.bind(this) ); this._actionRemovedId = this.device.action_group.connect( 'action-removed', this._onActionChanged.bind(this) ); this._actionStateChangedId = this.device.action_group.connect( 'action-state-changed', this._onStateChanged.bind(this) ); this._onActionChanged(this.device.action_group, 'connectivityReport'); // Cleanup on destroy this.connect('destroy', this._onDestroy); } _onActionChanged(action_group, action_name) { if (action_name !== 'connectivityReport') return; if (action_group.has_action('connectivityReport')) { const value = action_group.get_action_state('connectivityReport'); const [ cellular_network_type, cellular_network_type_icon, cellular_network_strength, cellular_network_strength_icon, hotspot_name, hotspot_bssid, ] = value.deepUnpack(); this._state = { cellular_network_type: cellular_network_type, cellular_network_type_icon: cellular_network_type_icon, cellular_network_strength: cellular_network_strength, cellular_network_strength_icon: cellular_network_strength_icon, hotspot_name: hotspot_name, hotspot_bssid: hotspot_bssid, }; } else { this._state = null; } this._sync(); } _onStateChanged(action_group, action_name, value) { if (action_name !== 'connectivityReport') return; const [ cellular_network_type, cellular_network_type_icon, cellular_network_strength, cellular_network_strength_icon, hotspot_name, hotspot_bssid, ] = value.deepUnpack(); this._state = { cellular_network_type: cellular_network_type, cellular_network_type_icon: cellular_network_type_icon, cellular_network_strength: cellular_network_strength, cellular_network_strength_icon: cellular_network_strength_icon, hotspot_name: hotspot_name, hotspot_bssid: hotspot_bssid, }; this._sync(); } _onDestroy(actor) { actor.device.action_group.disconnect(actor._actionAddedId); actor.device.action_group.disconnect(actor._actionRemovedId); actor.device.action_group.disconnect(actor._actionStateChangedId); } _sync() { this.visible = !!this._state; if (!this.visible) return; this.networkTypeIcon.icon_name = this._state.cellular_network_type_icon; this.signalStrengthIcon.icon_name = this._state.cellular_network_strength_icon; this.tooltip.text = this._state.cellular_network_type; } }); /** * A PopupMenu used as an information and control center for a device */ var Menu = class Menu extends PopupMenu.PopupMenuSection { constructor(params) { super(); Object.assign(this, params); this.actor.add_style_class_name('gsconnect-device-menu'); // Title this._title = new PopupMenu.PopupSeparatorMenuItem(this.device.name); this.addMenuItem(this._title); // Title -> Name this._title.label.style_class = 'gsconnect-device-name'; this._title.label.clutter_text.ellipsize = 0; this.device.bind_property( 'name', this._title.label, 'text', GObject.BindingFlags.SYNC_CREATE ); // Title -> Cellular Signal Strength this._signalStrength = new SignalStrength({device: this.device}); this._title.actor.add_child(this._signalStrength); // Title -> Battery this._battery = new Battery({device: this.device}); this._title.actor.add_child(this._battery); // Actions let actions; if (this.menu_type === 'icon') { actions = new GMenu.IconBox({ action_group: this.device.action_group, model: this.device.menu, }); } else if (this.menu_type === 'list') { actions = new GMenu.ListBox({ action_group: this.device.action_group, model: this.device.menu, }); } this.addMenuItem(actions); } isEmpty() { return false; } }; /** * An indicator representing a Device in the Status Area */ var Indicator = GObject.registerClass({ GTypeName: 'GSConnectDeviceIndicator', }, class Indicator extends PanelMenu.Button { _init(params) { super._init(0.0, `${params.device.name} Indicator`, false); Object.assign(this, params); // Device Icon this._icon = new St.Icon({ gicon: Extension.getIcon(this.device.icon_name), style_class: 'system-status-icon gsconnect-device-indicator', }); this.add_child(this._icon); // Menu const menu = new Menu({ device: this.device, menu_type: 'icon', }); this.menu.addMenuItem(menu); } }); ������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/shell/gmenu.js�����������������������������������������������0000664�0000000�0000000�00000044760�14215434441�0023071�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Atk = imports.gi.Atk; const Clutter = imports.gi.Clutter; const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; const St = imports.gi.St; const PopupMenu = imports.ui.popupMenu; const Extension = imports.misc.extensionUtils.getCurrentExtension(); const Tooltip = Extension.imports.shell.tooltip; /** * Get a dictionary of a GMenuItem's attributes * * @param {Gio.MenuModel} model - The menu model containing the item * @param {number} index - The index of the item in @model * @return {Object} A dictionary of the item's attributes */ function getItemInfo(model, index) { const info = { target: null, links: [], }; // let iter = model.iterate_item_attributes(index); while (iter.next()) { const name = iter.get_name(); let value = iter.get_value(); switch (name) { case 'icon': value = Gio.Icon.deserialize(value); if (value instanceof Gio.ThemedIcon) value = Extension.getIcon(value.names[0]); info[name] = value; break; case 'target': info[name] = value; break; default: info[name] = value.unpack(); } } // Submenus & Sections iter = model.iterate_item_links(index); while (iter.next()) { info.links.push({ name: iter.get_name(), value: iter.get_value(), }); } return info; } /** * */ var ListBox = class ListBox extends PopupMenu.PopupMenuSection { constructor(params) { super(); Object.assign(this, params); // Main Actor this.actor = new St.BoxLayout({ x_expand: true, clip_to_allocation: true, }); this.actor._delegate = this; // Item Box this.box.clip_to_allocation = true; this.box.x_expand = true; this.box.add_style_class_name('gsconnect-list-box'); this.box.set_pivot_point(1, 1); this.actor.add_child(this.box); // Submenu Container this.sub = new St.BoxLayout({ clip_to_allocation: true, vertical: false, visible: false, x_expand: true, }); this.sub.set_pivot_point(1, 1); this.sub._delegate = this; this.actor.add_child(this.sub); // Handle transitions this._boxTransitionsCompletedId = this.box.connect( 'transitions-completed', this._onTransitionsCompleted.bind(this) ); this._subTransitionsCompletedId = this.sub.connect( 'transitions-completed', this._onTransitionsCompleted.bind(this) ); // Handle keyboard navigation this._submenuCloseKeyId = this.sub.connect( 'key-press-event', this._onSubmenuCloseKey.bind(this) ); // Refresh the menu when mapped this._mappedId = this.actor.connect( 'notify::mapped', this._onMapped.bind(this) ); // Watch the model for changes this._itemsChangedId = this.model.connect( 'items-changed', this._onItemsChanged.bind(this) ); this._onItemsChanged(); } _onMapped(actor) { if (actor.mapped) { this._onItemsChanged(); // We use this instead of close() to avoid touching finalized objects } else { this.box.set_opacity(255); this.box.set_width(-1); this.box.set_height(-1); this.box.visible = true; this._submenu = null; this.sub.set_opacity(0); this.sub.set_width(0); this.sub.set_height(0); this.sub.visible = false; this.sub.get_children().map(menu => menu.hide()); } } _onSubmenuCloseKey(actor, event) { if (this.submenu && event.get_key_symbol() === Clutter.KEY_Left) { this.submenu.submenu_for.setActive(true); this.submenu = null; return Clutter.EVENT_STOP; } return Clutter.EVENT_PROPAGATE; } _onSubmenuOpenKey(actor, event) { const item = actor._delegate; if (item.submenu && event.get_key_symbol() === Clutter.KEY_Right) { this.submenu = item.submenu; item.submenu.firstMenuItem.setActive(true); } return Clutter.EVENT_PROPAGATE; } _onGMenuItemActivate(item, event) { this.emit('activate', item); if (item.submenu) { this.submenu = item.submenu; } else if (item.action_name) { this.action_group.activate_action( item.action_name, item.action_target ); this.itemActivated(); } } _addGMenuItem(info) { const item = new PopupMenu.PopupMenuItem(info.label); this.addMenuItem(item); if (info.action !== undefined) { item.action_name = info.action.split('.')[1]; item.action_target = info.target; item.actor.visible = this.action_group.get_action_enabled( item.action_name ); } // Modify the ::activate callback to invoke the GAction or submenu item.disconnect(item._activateId); item._activateId = item.connect( 'activate', this._onGMenuItemActivate.bind(this) ); return item; } _addGMenuSection(model) { const section = new ListBox({ model: model, action_group: this.action_group, }); this.addMenuItem(section); } _addGMenuSubmenu(model, item) { // Add an expander arrow to the item const arrow = PopupMenu.arrowIcon(St.Side.RIGHT); arrow.x_align = Clutter.ActorAlign.END; arrow.x_expand = true; item.actor.add_child(arrow); // Mark it as an expandable and open on right-arrow item.actor.add_accessible_state(Atk.StateType.EXPANDABLE); item.actor.connect( 'key-press-event', this._onSubmenuOpenKey.bind(this) ); // Create the submenu item.submenu = new ListBox({ model: model, action_group: this.action_group, submenu_for: item, _parent: this, }); item.submenu.actor.hide(); // Add to the submenu container this.sub.add_child(item.submenu.actor); } _onItemsChanged(model, position, removed, added) { // Clear the menu this.removeAll(); this.sub.get_children().map(child => child.destroy()); for (let i = 0, len = this.model.get_n_items(); i < len; i++) { const info = getItemInfo(this.model, i); let item; // A regular item if (info.hasOwnProperty('label')) item = this._addGMenuItem(info); for (const link of info.links) { // Submenu if (link.name === 'submenu') { this._addGMenuSubmenu(link.value, item); // Section } else if (link.name === 'section') { this._addGMenuSection(link.value); // len is length starting at 1 if (i + 1 < len) this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); } } } // If this is a submenu of another item... if (this.submenu_for) { // Prepend an "<= Go Back" item, bold with a unicode arrow const prev = new PopupMenu.PopupMenuItem(this.submenu_for.label.text); prev.label.style = 'font-weight: bold;'; const prevArrow = PopupMenu.arrowIcon(St.Side.LEFT); prev.replace_child(prev._ornamentLabel, prevArrow); this.addMenuItem(prev, 0); // Modify the ::activate callback to close the submenu prev.disconnect(prev._activateId); prev._activateId = prev.connect('activate', (item, event) => { this.emit('activate', item); this._parent.submenu = null; }); } } _onTransitionsCompleted(actor) { if (this.submenu) { this.box.visible = false; } else { this.sub.visible = false; this.sub.get_children().map(menu => menu.hide()); } } get submenu() { return this._submenu || null; } set submenu(submenu) { // Get the current allocation to hold the menu width const allocation = this.actor.allocation; const width = Math.max(0, allocation.x2 - allocation.x1); // Prepare the appropriate child for tweening if (submenu) { this.sub.set_opacity(0); this.sub.set_width(0); this.sub.set_height(0); this.sub.visible = true; } else { this.box.set_opacity(0); this.box.set_width(0); this.sub.set_height(0); this.box.visible = true; } // Setup the animation this.box.save_easing_state(); this.box.set_easing_mode(Clutter.AnimationMode.EASE_IN_OUT_CUBIC); this.box.set_easing_duration(250); this.sub.save_easing_state(); this.sub.set_easing_mode(Clutter.AnimationMode.EASE_IN_OUT_CUBIC); this.sub.set_easing_duration(250); if (submenu) { submenu.actor.show(); this.sub.set_opacity(255); this.sub.set_width(width); this.sub.set_height(-1); this.box.set_opacity(0); this.box.set_width(0); this.box.set_height(0); } else { this.box.set_opacity(255); this.box.set_width(width); this.box.set_height(-1); this.sub.set_opacity(0); this.sub.set_width(0); this.sub.set_height(0); } // Reset the animation this.box.restore_easing_state(); this.sub.restore_easing_state(); // this._submenu = submenu; } destroy() { this.actor.disconnect(this._mappedId); this.box.disconnect(this._boxTransitionsCompletedId); this.sub.disconnect(this._subTransitionsCompletedId); this.sub.disconnect(this._submenuCloseKeyId); this.model.disconnect(this._itemsChangedId); super.destroy(); } }; /** * A St.Button subclass for iconic GMenu items */ var IconButton = GObject.registerClass({ GTypeName: 'GSConnectShellIconButton', }, class Button extends St.Button { _init(params) { super._init({ style_class: 'gsconnect-icon-button', can_focus: true, }); Object.assign(this, params); // Item attributes if (params.info.hasOwnProperty('action')) this.action_name = params.info.action.split('.')[1]; if (params.info.hasOwnProperty('target')) this.action_target = params.info.target; if (params.info.hasOwnProperty('label')) { this.tooltip = new Tooltip.Tooltip({ parent: this, markup: params.info.label, }); this.accessible_name = params.info.label; } if (params.info.hasOwnProperty('icon')) this.child = new St.Icon({gicon: params.info.icon}); // Submenu for (const link of params.info.links) { if (link.name === 'submenu') { this.add_accessible_state(Atk.StateType.EXPANDABLE); this.toggle_mode = true; this.connect('notify::checked', this._onChecked); this.submenu = new ListBox({ model: link.value, action_group: this.action_group, _parent: this._parent, }); this.submenu.actor.style_class = 'popup-sub-menu'; this.submenu.actor.visible = false; } } } // This is (reliably?) emitted before ::clicked _onChecked(button) { if (button.checked) { button.add_accessible_state(Atk.StateType.EXPANDED); button.add_style_pseudo_class('active'); } else { button.remove_accessible_state(Atk.StateType.EXPANDED); button.remove_style_pseudo_class('active'); } } // This is (reliably?) emitted after notify::checked vfunc_clicked(clicked_button) { // Unless this has a submenu, activate the action and close the menu if (!this.toggle_mode) { this._parent._getTopMenu().close(); this.action_group.activate_action( this.action_name, this.action_target ); // StButton.checked has already been toggled so we're opening } else if (this.checked) { this._parent.submenu = this.submenu; // If this is the active submenu being closed, animate-close it } else if (this._parent.submenu === this.submenu) { this._parent.submenu = null; } } }); var IconBox = class IconBox extends PopupMenu.PopupMenuSection { constructor(params) { super(); Object.assign(this, params); // Main Actor this.actor = new St.BoxLayout({ vertical: true, x_expand: true, }); this.actor._delegate = this; // Button Box this.box._delegate = this; this.box.style_class = 'gsconnect-icon-box'; this.box.vertical = false; this.actor.add_child(this.box); // Submenu Container this.sub = new St.BoxLayout({ clip_to_allocation: true, vertical: true, x_expand: true, }); this.sub.connect('transitions-completed', this._onTransitionsCompleted); this.sub._delegate = this; this.actor.add_child(this.sub); // Track menu items so we can use ::items-changed this._menu_items = new Map(); // PopupMenu this._mappedId = this.actor.connect( 'notify::mapped', this._onMapped.bind(this) ); // GMenu this._itemsChangedId = this.model.connect( 'items-changed', this._onItemsChanged.bind(this) ); // GActions this._actionAddedId = this.action_group.connect( 'action-added', this._onActionChanged.bind(this) ); this._actionEnabledChangedId = this.action_group.connect( 'action-enabled-changed', this._onActionChanged.bind(this) ); this._actionRemovedId = this.action_group.connect( 'action-removed', this._onActionChanged.bind(this) ); } destroy() { this.actor.disconnect(this._mappedId); this.model.disconnect(this._itemsChangedId); this.action_group.disconnect(this._actionAddedId); this.action_group.disconnect(this._actionEnabledChangedId); this.action_group.disconnect(this._actionRemovedId); super.destroy(); } get submenu() { return this._submenu || null; } set submenu(submenu) { if (submenu) { for (const button of this.box.get_children()) { if (button.submenu && this._submenu && button.submenu !== submenu) { button.checked = false; button.submenu.actor.hide(); } } this.sub.set_height(0); submenu.actor.show(); } this.sub.save_easing_state(); this.sub.set_easing_duration(250); this.sub.set_easing_mode(Clutter.AnimationMode.EASE_IN_OUT_CUBIC); this.sub.set_height(submenu ? submenu.actor.get_preferred_size()[1] : 0); this.sub.restore_easing_state(); this._submenu = submenu; } _onMapped(actor) { if (!actor.mapped) { this._submenu = null; for (const button of this.box.get_children()) button.checked = false; for (const submenu of this.sub.get_children()) submenu.hide(); } } _onActionChanged(group, name, enabled) { const menuItem = this._menu_items.get(name); if (menuItem !== undefined) menuItem.visible = group.get_action_enabled(name); } _onItemsChanged(model, position, removed, added) { // Remove items while (removed > 0) { const button = this.box.get_child_at_index(position); const action_name = button.action_name; if (button.submenu) button.submenu.destroy(); button.destroy(); this._menu_items.delete(action_name); removed--; } // Add items for (let i = 0; i < added; i++) { const index = position + i; // Create an iconic button const button = new IconButton({ action_group: this.action_group, info: getItemInfo(model, index), // NOTE: Because this doesn't derive from a PopupMenu class // it lacks some things its parent will expect from it _parent: this, _delegate: null, }); // Set the visibility based on the enabled state if (button.action_name !== undefined) { button.visible = this.action_group.get_action_enabled( button.action_name ); } // If it has a submenu, add it as a sibling if (button.submenu) this.sub.add_child(button.submenu.actor); // Track the item if it has an action if (button.action_name !== undefined) this._menu_items.set(button.action_name, button); // Insert it in the box at the defined position this.box.insert_child_at_index(button, index); } } _onTransitionsCompleted(actor) { const menu = actor._delegate; for (const button of menu.box.get_children()) { if (button.submenu && button.submenu !== menu.submenu) { button.checked = false; button.submenu.actor.hide(); } } menu.sub.set_height(-1); } // PopupMenu.PopupMenuBase overrides isEmpty() { return (this.box.get_children().length === 0); } _setParent(parent) { super._setParent(parent); this._onItemsChanged(this.model, 0, 0, this.model.get_n_items()); } }; ����������������gnome-shell-extension-gsconnect-50/src/shell/keybindings.js�����������������������������������������0000664�0000000�0000000�00000006102�14215434441�0024250�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Config = imports.misc.config; const Main = imports.ui.main; const Meta = imports.gi.Meta; const Shell = imports.gi.Shell; /** * Keybindings.Manager is a simple convenience class for managing keyboard * shortcuts in GNOME Shell. You bind a shortcut using add(), which on success * will return a non-zero action id that can later be used with remove() to * unbind the shortcut. * * Accelerators are accepted in the form returned by Gtk.accelerator_name() and * callbacks are invoked directly, so should be complete closures. * * References: * https://developer.gnome.org/gtk3/stable/gtk3-Keyboard-Accelerators.html * https://developer.gnome.org/meta/stable/MetaDisplay.html * https://developer.gnome.org/meta/stable/meta-MetaKeybinding.html * https://gitlab.gnome.org/GNOME/gnome-shell/blob/master/js/ui/windowManager.js#L1093-1112 */ var Manager = class Manager { constructor() { this._keybindings = new Map(); this._acceleratorActivatedId = global.display.connect( 'accelerator-activated', this._onAcceleratorActivated.bind(this) ); } _onAcceleratorActivated(display, action, inputDevice, timestamp) { try { const binding = this._keybindings.get(action); if (binding !== undefined) binding.callback(); } catch (e) { logError(e); } } /** * Add a keybinding with callback * * @param {string} accelerator - An accelerator in the form '<Control>q' * @param {Function} callback - A callback for the accelerator * @return {number} A non-zero action id on success, or 0 on failure */ add(accelerator, callback) { try { const action = global.display.grab_accelerator(accelerator, 0); if (action === Meta.KeyBindingAction.NONE) throw new Error(`Failed to add keybinding: '${accelerator}'`); const name = Meta.external_binding_name_for_action(action); Main.wm.allowKeybinding(name, Shell.ActionMode.ALL); this._keybindings.set(action, {name: name, callback: callback}); return action; } catch (e) { logError(e); } } /** * Remove a keybinding * * @param {number} action - A non-zero action id returned by add() */ remove(action) { try { const binding = this._keybindings.get(action); global.display.ungrab_accelerator(action); Main.wm.allowKeybinding(binding.name, Shell.ActionMode.NONE); this._keybindings.delete(action); } catch (e) { logError(new Error(`Failed to remove keybinding: ${e.message}`)); } } /** * Remove all keybindings */ removeAll() { for (const action of this._keybindings.keys()) this.remove(action); } /** * Destroy the keybinding manager and remove all keybindings */ destroy() { global.display.disconnect(this._acceleratorActivatedId); this.removeAll(); } }; ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/shell/notification.js����������������������������������������0000664�0000000�0000000�00000034044�14215434441�0024436�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const St = imports.gi.St; const Main = imports.ui.main; const MessageTray = imports.ui.messageTray; const NotificationDaemon = imports.ui.notificationDaemon; const Extension = imports.misc.extensionUtils.getCurrentExtension(); // eslint-disable-next-line no-redeclare const _ = Extension._; const APP_ID = 'org.gnome.Shell.Extensions.GSConnect'; const APP_PATH = '/org/gnome/Shell/Extensions/GSConnect'; // deviceId Pattern (<device-id>|<remote-id>) const DEVICE_REGEX = new RegExp(/^([^|]+)\|([\s\S]+)$/); // requestReplyId Pattern (<device-id>|<remote-id>)|<reply-id>) const REPLY_REGEX = new RegExp(/^([^|]+)\|([\s\S]+)\|([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$/, 'i'); /** * A slightly modified Notification Banner with an entry field */ const NotificationBanner = GObject.registerClass({ GTypeName: 'GSConnectNotificationBanner', }, class NotificationBanner extends MessageTray.NotificationBanner { _init(notification) { super._init(notification); if (notification.requestReplyId !== undefined) this._addReplyAction(); } _addReplyAction() { if (!this._buttonBox) { this._buttonBox = new St.BoxLayout({ style_class: 'notification-actions', x_expand: true, }); this.setActionArea(this._buttonBox); global.focus_manager.add_group(this._buttonBox); } // Reply Button const button = new St.Button({ style_class: 'notification-button', label: _('Reply'), x_expand: true, can_focus: true, }); button.connect( 'clicked', this._onEntryRequested.bind(this) ); this._buttonBox.add_child(button); // Reply Entry this._replyEntry = new St.Entry({ can_focus: true, hint_text: _('Type a message'), style_class: 'chat-response', x_expand: true, visible: false, }); this._buttonBox.add_child(this._replyEntry); } _onEntryRequested(button) { this.focused = true; for (const child of this._buttonBox.get_children()) child.visible = (child === this._replyEntry); // Release the notification focus with the entry focus this._replyEntry.connect( 'key-focus-out', this._onEntryDismissed.bind(this) ); this._replyEntry.clutter_text.connect( 'activate', this._onEntryActivated.bind(this) ); this._replyEntry.grab_key_focus(); } _onEntryDismissed(entry) { this.focused = false; this.emit('unfocused'); } _onEntryActivated(clutter_text) { // Refuse to send empty replies if (clutter_text.text === '') return; // Copy the text, then clear the entry const text = clutter_text.text; clutter_text.text = ''; const {deviceId, requestReplyId} = this.notification; const target = new GLib.Variant('(ssbv)', [ deviceId, 'replyNotification', true, new GLib.Variant('(ssa{ss})', [requestReplyId, text, {}]), ]); const platformData = NotificationDaemon.getPlatformData(); Gio.DBus.session.call( APP_ID, APP_PATH, 'org.freedesktop.Application', 'ActivateAction', GLib.Variant.new('(sava{sv})', ['device', [target], platformData]), null, Gio.DBusCallFlags.NO_AUTO_START, -1, null, (connection, res) => { try { connection.call_finish(res); } catch (e) { // Silence errors } } ); this.close(); } }); /** * A custom notification source for spawning notifications and closing device * notifications. This source isn't actually used, but it's methods are patched * into existing sources. */ const Source = GObject.registerClass({ GTypeName: 'GSConnectNotificationSource', }, class Source extends NotificationDaemon.GtkNotificationDaemonAppSource { _closeGSConnectNotification(notification, reason) { if (reason !== MessageTray.NotificationDestroyedReason.DISMISSED) return; // Avoid sending the request multiple times if (notification._remoteClosed || notification.remoteId === undefined) return; notification._remoteClosed = true; const target = new GLib.Variant('(ssbv)', [ notification.deviceId, 'closeNotification', true, new GLib.Variant('s', notification.remoteId), ]); const platformData = NotificationDaemon.getPlatformData(); Gio.DBus.session.call( APP_ID, APP_PATH, 'org.freedesktop.Application', 'ActivateAction', GLib.Variant.new('(sava{sv})', ['device', [target], platformData]), null, Gio.DBusCallFlags.NO_AUTO_START, -1, null, (connection, res) => { try { connection.call_finish(res); } catch (e) { // If we fail, reset in case we can try again notification._remoteClosed = false; } } ); } /* * Override to control notification spawning */ addNotification(notificationId, notificationParams, showBanner) { this._notificationPending = true; // Parse the id to determine if it's a repliable notification, device // notification or a regular local notification let idMatch, deviceId, requestReplyId, remoteId, localId; if ((idMatch = REPLY_REGEX.exec(notificationId))) { [, deviceId, remoteId, requestReplyId] = idMatch; localId = `${deviceId}|${remoteId}`; } else if ((idMatch = DEVICE_REGEX.exec(notificationId))) { [, deviceId, remoteId] = idMatch; localId = `${deviceId}|${remoteId}`; } else { localId = notificationId; } // Fix themed icons if (notificationParams.icon) { let gicon = Gio.Icon.deserialize(notificationParams.icon); if (gicon instanceof Gio.ThemedIcon) { gicon = Extension.getIcon(gicon.names[0]); notificationParams.icon = gicon.serialize(); } } let notification = this._notifications[localId]; // Check if this is a repeat if (notification) { notification.requestReplyId = requestReplyId; // Bail early If @notificationParams represents an exact repeat const title = notificationParams.title.unpack(); const body = notificationParams.body ? notificationParams.body.unpack() : null; if (notification.title === title && notification.bannerBodyText === body) { this._notificationPending = false; return; } notification.title = title; notification.bannerBodyText = body; // Device Notification } else if (idMatch) { notification = this._createNotification(notificationParams); notification.deviceId = deviceId; notification.remoteId = remoteId; notification.requestReplyId = requestReplyId; notification.connect('destroy', (notification, reason) => { this._closeGSConnectNotification(notification, reason); delete this._notifications[localId]; }); this._notifications[localId] = notification; // Service Notification } else { notification = this._createNotification(notificationParams); notification.connect('destroy', (notification, reason) => { delete this._notifications[localId]; }); this._notifications[localId] = notification; } if (showBanner) this.showNotification(notification); else this.pushNotification(notification); this._notificationPending = false; } /* * Override to raise the usual notification limit (3) */ pushNotification(notification) { if (this.notifications.includes(notification)) return; while (this.notifications.length >= 10) this.notifications.shift().destroy(MessageTray.NotificationDestroyedReason.EXPIRED); notification.connect('destroy', this._onNotificationDestroy.bind(this)); notification.connect('notify::acknowledged', this.countUpdated.bind(this)); this.notifications.push(notification); this.emit('notification-added', notification); this.countUpdated(); } createBanner(notification) { return new NotificationBanner(notification); } }); /** * If there is an active GtkNotificationDaemonAppSource for GSConnect when the * extension is loaded, it has to be patched in place. */ function patchGSConnectNotificationSource() { const source = Main.notificationDaemon._gtkNotificationDaemon._sources[APP_ID]; if (source !== undefined) { // Patch in the subclassed methods source._closeGSConnectNotification = Source.prototype._closeGSConnectNotification; source.addNotification = Source.prototype.addNotification; source.pushNotification = Source.prototype.pushNotification; source.createBanner = Source.prototype.createBanner; // Connect to existing notifications for (const notification of Object.values(source._notifications)) { const _id = notification.connect('destroy', (notification, reason) => { source._closeGSConnectNotification(notification, reason); notification.disconnect(_id); }); } } } /** * Wrap GtkNotificationDaemon._ensureAppSource() to patch GSConnect's app source * https://gitlab.gnome.org/GNOME/gnome-shell/blob/master/js/ui/notificationDaemon.js#L742-755 */ const __ensureAppSource = NotificationDaemon.GtkNotificationDaemon.prototype._ensureAppSource; // eslint-disable-next-line func-style const _ensureAppSource = function (appId) { const source = __ensureAppSource.call(this, appId); if (source._appId === APP_ID) { source._closeGSConnectNotification = Source.prototype._closeGSConnectNotification; source.addNotification = Source.prototype.addNotification; source.pushNotification = Source.prototype.pushNotification; source.createBanner = Source.prototype.createBanner; } return source; }; function patchGtkNotificationDaemon() { NotificationDaemon.GtkNotificationDaemon.prototype._ensureAppSource = _ensureAppSource; } function unpatchGtkNotificationDaemon() { NotificationDaemon.GtkNotificationDaemon.prototype._ensureAppSource = __ensureAppSource; } /** * We patch other Gtk notification sources so we can notify remote devices when * notifications have been closed locally. */ const _addNotification = NotificationDaemon.GtkNotificationDaemonAppSource.prototype.addNotification; function patchGtkNotificationSources() { // This should diverge as little as possible from the original // eslint-disable-next-line func-style const addNotification = function (notificationId, notificationParams, showBanner) { this._notificationPending = true; if (this._notifications[notificationId]) this._notifications[notificationId].destroy(MessageTray.NotificationDestroyedReason.REPLACED); const notification = this._createNotification(notificationParams); notification.connect('destroy', (notification, reason) => { this._withdrawGSConnectNotification(notification, reason); delete this._notifications[notificationId]; }); this._notifications[notificationId] = notification; if (showBanner) this.showNotification(notification); else this.pushNotification(notification); this._notificationPending = false; }; // eslint-disable-next-line func-style const _withdrawGSConnectNotification = function (id, notification, reason) { if (reason !== MessageTray.NotificationDestroyedReason.DISMISSED) return; // Avoid sending the request multiple times if (notification._remoteWithdrawn) return; notification._remoteWithdrawn = true; // Recreate the notification id as it would've been sent const target = new GLib.Variant('(ssbv)', [ '*', 'withdrawNotification', true, new GLib.Variant('s', `gtk|${this._appId}|${id}`), ]); const platformData = NotificationDaemon.getPlatformData(); Gio.DBus.session.call( APP_ID, APP_PATH, 'org.freedesktop.Application', 'ActivateAction', GLib.Variant.new('(sava{sv})', ['device', [target], platformData]), null, Gio.DBusCallFlags.NO_AUTO_START, -1, null, (connection, res) => { try { connection.call_finish(res); } catch (e) { // If we fail, reset in case we can try again notification._remoteWithdrawn = false; } } ); }; NotificationDaemon.GtkNotificationDaemonAppSource.prototype.addNotification = addNotification; NotificationDaemon.GtkNotificationDaemonAppSource.prototype._withdrawGSConnectNotification = _withdrawGSConnectNotification; } function unpatchGtkNotificationSources() { NotificationDaemon.GtkNotificationDaemonAppSource.prototype.addNotification = _addNotification; delete NotificationDaemon.GtkNotificationDaemonAppSource.prototype._withdrawGSConnectNotification; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/shell/tooltip.js���������������������������������������������0000664�0000000�0000000�00000017545�14215434441�0023451�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Clutter = imports.gi.Clutter; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Pango = imports.gi.Pango; const St = imports.gi.St; const Main = imports.ui.main; /** * An StTooltip for ClutterActors * * Adapted from: https://github.com/RaphaelRochet/applications-overview-tooltip * See also: https://github.com/GNOME/gtk/blob/master/gtk/gtktooltip.c */ var TOOLTIP_BROWSE_ID = 0; var TOOLTIP_BROWSE_MODE = false; var Tooltip = class Tooltip { constructor(params) { Object.assign(this, params); this._bin = null; this._hoverTimeoutId = 0; this._showing = false; this._destroyId = this.parent.connect( 'destroy', this.destroy.bind(this) ); this._hoverId = this.parent.connect( 'notify::hover', this._onHover.bind(this) ); this._buttonPressEventId = this.parent.connect( 'button-press-event', this._hide.bind(this) ); } get custom() { if (this._custom === undefined) this._custom = null; return this._custom; } set custom(actor) { this._custom = actor; this._markup = null; this._text = null; if (this._showing) this._show(); } get gicon() { if (this._gicon === undefined) this._gicon = null; return this._gicon; } set gicon(gicon) { this._gicon = gicon; if (this._showing) this._show(); } get icon() { return (this.gicon) ? this.gicon.name : null; } set icon(icon_name) { if (!icon_name) this.gicon = null; else this.gicon = new Gio.ThemedIcon({name: icon_name}); } get markup() { if (this._markup === undefined) this._markup = null; return this._markup; } set markup(text) { this._markup = text; this._text = null; if (this._showing) this._show(); } get text() { if (this._text === undefined) this._text = null; return this._text; } set text(text) { this._markup = null; this._text = text; if (this._showing) this._show(); } get x_offset() { if (this._x_offset === undefined) this._x_offset = 0; return this._x_offset; } set x_offset(offset) { this._x_offset = (Number.isInteger(offset)) ? offset : 0; } get y_offset() { if (this._y_offset === undefined) this._y_offset = 0; return this._y_offset; } set y_offset(offset) { this._y_offset = (Number.isInteger(offset)) ? offset : 0; } _show() { if (this.text === null && this.markup === null) return this._hide(); if (this._bin === null) { this._bin = new St.Bin({ style_class: 'osd-window gsconnect-tooltip', opacity: 232, }); if (this.custom) { this._bin.child = this.custom; } else { this._bin.child = new St.BoxLayout({vertical: false}); if (this.gicon) { this._bin.child.icon = new St.Icon({ gicon: this.gicon, y_align: St.Align.START, }); this._bin.child.icon.set_y_align(Clutter.ActorAlign.START); this._bin.child.add_child(this._bin.child.icon); } this.label = new St.Label({text: this.markup || this.text}); this.label.clutter_text.line_wrap = true; this.label.clutter_text.line_wrap_mode = Pango.WrapMode.WORD; this.label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; this.label.clutter_text.use_markup = (this.markup); this._bin.child.add_child(this.label); } Main.layoutManager.uiGroup.add_child(this._bin); Main.layoutManager.uiGroup.set_child_above_sibling(this._bin, null); } else if (this.custom) { this._bin.child = this.custom; } else { if (this._bin.child.icon) this._bin.child.icon.destroy(); if (this.gicon) { this._bin.child.icon = new St.Icon({gicon: this.gicon}); this._bin.child.insert_child_at_index(this._bin.child.icon, 0); } this.label.clutter_text.text = this.markup || this.text; this.label.clutter_text.use_markup = (this.markup); } // Position tooltip let [x, y] = this.parent.get_transformed_position(); x = (x + (this.parent.width / 2)) - Math.round(this._bin.width / 2); x += this.x_offset; y += this.y_offset; // Show tooltip if (this._showing) { this._bin.ease({ x: x, y: y, time: 0.15, transition: Clutter.AnimationMode.EASE_OUT_QUAD, }); } else { this._bin.set_position(x, y); this._bin.ease({ opacity: 232, time: 0.15, transition: Clutter.AnimationMode.EASE_OUT_QUAD, }); this._showing = true; } // Enable browse mode TOOLTIP_BROWSE_MODE = true; if (TOOLTIP_BROWSE_ID) { GLib.source_remove(TOOLTIP_BROWSE_ID); TOOLTIP_BROWSE_ID = 0; } if (this._hoverTimeoutId) { GLib.source_remove(this._hoverTimeoutId); this._hoverTimeoutId = 0; } } _hide() { if (this._bin) { this._bin.ease({ opacity: 0, time: 0.10, transition: Clutter.AnimationMode.EASE_OUT_QUAD, onComplete: () => { Main.layoutManager.uiGroup.remove_actor(this._bin); if (this.custom) this._bin.remove_child(this.custom); this._bin.destroy(); this._bin = null; }, }); } TOOLTIP_BROWSE_ID = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => { TOOLTIP_BROWSE_MODE = false; TOOLTIP_BROWSE_ID = 0; return false; }); if (this._hoverTimeoutId) { GLib.source_remove(this._hoverTimeoutId); this._hoverTimeoutId = 0; } this._showing = false; this._hoverTimeoutId = 0; } _onHover() { if (this.parent.hover) { if (!this._hoverTimeoutId) { if (this._showing) { this._show(); } else { this._hoverTimeoutId = GLib.timeout_add( GLib.PRIORITY_DEFAULT, (TOOLTIP_BROWSE_MODE) ? 60 : 500, () => { this._show(); this._hoverTimeoutId = 0; return false; } ); } } } else { this._hide(); } } destroy() { this.parent.disconnect(this._destroyId); this.parent.disconnect(this._hoverId); this.parent.disconnect(this._buttonPressEventId); if (this.custom) this.custom.destroy(); if (this._bin) { Main.layoutManager.uiGroup.remove_actor(this._bin); this._bin.destroy(); } if (TOOLTIP_BROWSE_ID) { GLib.source_remove(TOOLTIP_BROWSE_ID); TOOLTIP_BROWSE_ID = 0; } if (this._hoverTimeoutId) { GLib.source_remove(this._hoverTimeoutId); this._hoverTimeoutId = 0; } } }; �����������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/shell/utils.js�����������������������������������������������0000664�0000000�0000000�00000016735�14215434441�0023117�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const ByteArray = imports.byteArray; const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const Extension = imports.misc.extensionUtils.getCurrentExtension(); const Config = Extension.imports.config; /** * Get a themed icon, using fallbacks from GSConnect's GResource when necessary. * * @param {string} name - A themed icon name * @return {Gio.Icon} A themed icon */ function getIcon(name) { if (getIcon._resource === undefined) { // Setup the desktop icons const settings = imports.gi.St.Settings.get(); getIcon._desktop = new imports.gi.Gtk.IconTheme(); getIcon._desktop.set_custom_theme(settings.gtk_icon_theme); settings.connect('notify::gtk-icon-theme', (settings_, key_) => { getIcon._desktop.set_custom_theme(settings_.gtk_icon_theme); }); // Preload our fallbacks const iconPath = 'resource://org/gnome/Shell/Extensions/GSConnect/icons'; const iconNames = [ 'org.gnome.Shell.Extensions.GSConnect', 'org.gnome.Shell.Extensions.GSConnect-symbolic', 'computer-symbolic', 'laptop-symbolic', 'smartphone-symbolic', 'tablet-symbolic', 'tv-symbolic', 'phonelink-ring-symbolic', 'sms-symbolic', ]; getIcon._resource = {}; for (const iconName of iconNames) { getIcon._resource[iconName] = new Gio.FileIcon({ file: Gio.File.new_for_uri(`${iconPath}/${iconName}.svg`), }); } } // Check the desktop icon theme if (getIcon._desktop.has_icon(name)) return new Gio.ThemedIcon({name: name}); // Check our GResource if (getIcon._resource[name] !== undefined) return getIcon._resource[name]; // Fallback to hoping it's in the theme somewhere return new Gio.ThemedIcon({name: name}); } /** * Get the contents of a GResource file, replacing `@PACKAGE_DATADIR@` where * necessary. * * @param {string} relativePath - A path relative to GSConnect's resource path * @return {string} The file contents as a string */ function getResource(relativePath) { try { const bytes = Gio.resources_lookup_data( GLib.build_filenamev([Config.APP_PATH, relativePath]), Gio.ResourceLookupFlags.NONE ); const source = ByteArray.toString(bytes.toArray()); return source.replace('@PACKAGE_DATADIR@', Config.PACKAGE_DATADIR); } catch (e) { logError(e, 'GSConnect'); return null; } } /** * Install file contents, to an absolute directory path. * * @param {string} dirname - An absolute directory path * @param {string} basename - The file name * @param {string} contents - The file contents * @return {boolean} A success boolean */ function _installFile(dirname, basename, contents) { try { const filename = GLib.build_filenamev([dirname, basename]); GLib.mkdir_with_parents(dirname, 0o755); return GLib.file_set_contents(filename, contents); } catch (e) { logError(e, 'GSConnect'); return false; } } /** * Install file contents from a GResource, to an absolute directory path. * * @param {string} dirname - An absolute directory path * @param {string} basename - The file name * @param {string} relativePath - A path relative to GSConnect's resource path * @return {boolean} A success boolean */ function _installResource(dirname, basename, relativePath) { try { const contents = getResource(relativePath); return _installFile(dirname, basename, contents); } catch (e) { logError(e, 'GSConnect'); return false; } } /** * Install the files necessary for the GSConnect service to run. */ function installService() { const confDir = GLib.get_user_config_dir(); const dataDir = GLib.get_user_data_dir(); const homeDir = GLib.get_home_dir(); // DBus Service const dbusDir = GLib.build_filenamev([dataDir, 'dbus-1', 'services']); const dbusFile = `${Config.APP_ID}.service`; // Desktop Entry const appDir = GLib.build_filenamev([dataDir, 'applications']); const appFile = `${Config.APP_ID}.desktop`; const appPrefsFile = `${Config.APP_ID}.Preferences.desktop`; // Application Icon const iconDir = GLib.build_filenamev([dataDir, 'icons', 'hicolor', 'scalable', 'apps']); const iconFull = `${Config.APP_ID}.svg`; const iconSym = `${Config.APP_ID}-symbolic.svg`; // File Manager Extensions const fileManagers = [ [`${dataDir}/nautilus-python/extensions`, 'nautilus-gsconnect.py'], [`${dataDir}/nemo-python/extensions`, 'nemo-gsconnect.py'], ]; // WebExtension Manifests const manifestFile = 'org.gnome.shell.extensions.gsconnect.json'; const google = getResource(`webextension/${manifestFile}.google.in`); const mozilla = getResource(`webextension/${manifestFile}.mozilla.in`); const manifests = [ [`${confDir}/chromium/NativeMessagingHosts/`, google], [`${confDir}/google-chrome/NativeMessagingHosts/`, google], [`${confDir}/google-chrome-beta/NativeMessagingHosts/`, google], [`${confDir}/google-chrome-unstable/NativeMessagingHosts/`, google], [`${confDir}/BraveSoftware/Brave-Browser/NativeMessagingHosts/`, google], [`${confDir}/BraveSoftware/Brave-Browser-Nightly/NativeMessagingHosts/`, google], [`${homeDir}/.mozilla/native-messaging-hosts/`, mozilla], [`${homeDir}/.config/microsoft-edge-dev/NativeMessagingHosts`, google], [`${homeDir}/.config/microsoft-edge-beta/NativeMessagingHosts`, google], ]; // If running as a user extension, ensure the DBus service, desktop entry, // file manager scripts, and WebExtension manifests are installed. if (Config.IS_USER) { // DBus Service if (!_installResource(dbusDir, dbusFile, `${dbusFile}.in`)) throw Error('GSConnect: Failed to install DBus Service'); // Desktop Entries _installResource(appDir, appFile, appFile); _installResource(appDir, appPrefsFile, appPrefsFile); // Application Icon _installResource(iconDir, iconFull, `icons/${iconFull}`); _installResource(iconDir, iconSym, `icons/${iconSym}`); // File Manager Extensions const target = `${Config.PACKAGE_DATADIR}/nautilus-gsconnect.py`; for (const [dir, name] of fileManagers) { const script = Gio.File.new_for_path(GLib.build_filenamev([dir, name])); if (!script.query_exists(null)) { GLib.mkdir_with_parents(dir, 0o755); script.make_symbolic_link(target, null); } } // WebExtension Manifests for (const [dirname, contents] of manifests) _installFile(dirname, manifestFile, contents); // Otherwise, if running as a system extension, ensure anything previously // installed when running as a user extension is removed. } else { GLib.unlink(GLib.build_filenamev([dbusDir, dbusFile])); GLib.unlink(GLib.build_filenamev([appDir, appFile])); GLib.unlink(GLib.build_filenamev([appDir, appPrefsFile])); GLib.unlink(GLib.build_filenamev([iconDir, iconFull])); GLib.unlink(GLib.build_filenamev([iconDir, iconSym])); for (const [dir, name] of fileManagers) GLib.unlink(GLib.build_filenamev([dir, name])); for (const manifest of manifests) GLib.unlink(GLib.build_filenamev([manifest[0], manifestFile])); } } �����������������������������������gnome-shell-extension-gsconnect-50/src/stylesheet.css�����������������������������������������������0000664�0000000�0000000�00000004445�14215434441�0023210�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ /* Device Menu PopupMenu.PopupMenuSection.gsconnect-device-section PopupMenu.PopupMenuSection.gsconnect-device-menu PopupMenu.PopupSeparatorMenuItem StLabel.gsconnect-device-name StBoxLayout.gsconnect-device-battery PopupMenu.PopupMenuSection StBoxLayout.gsconnect-list-box StBoxLayout (Submenu Container) */ .gsconnect-device-section { } /* Title Bar */ .gsconnect-device-name { font-weight: bold; } .gsconnect-device-menu .popup-separator-menu-item { margin-left: 0; margin-right: 0; } /* Battery Widget */ .gsconnect-device-battery { spacing: 3px; } .gsconnect-device-battery StLabel { font-size: 0.75em; } .gsconnect-device-battery StIcon { icon-size: 16px; } /* Signal Strength Widget */ .gsconnect-device-signal-strength { spacing: 3px; } .gsconnect-device-signal-strength StLabel { font-size: 0.75em; } .gsconnect-device-signal-strength StIcon { icon-size: 16px; } /* List Box */ .gsconnect-list-box { } /* Device Panel Indicator PanelMenu.Button.gsconnect-device-indicator PopupMenu.PopupMenu PopupMenu.PopupMenuSection.gsconnect-device-menu PopupMenu.PopupSeparatorMenuItem StLabel.gsconnect-device-name StBoxLayout.gsconnect-device-battery PopupMenu.PopupMenuSection StBoxLayout.gsconnect-icon-box StBoxLayout (Submenu Container) */ .gsconnect-device-indicator { -st-icon-style: symbolic; } /* Icon Box */ .gsconnect-icon-box { margin: 0em 2em 0.5em; spacing: 6px; } .gsconnect-icon-button { border-radius: 1em; padding: 0.5em; } .gsconnect-icon-button:hover, .gsconnect-icon-button:focus { background-color: rgba(255, 255, 255, 0.125); } .gsconnect-icon-button StIcon { icon-size: 1em; } /* Tooltip StBin.gsconnect-tooltip (inherits from .osd-window) StBoxLayout || [ Custom ClutterActor ] StIcon StLabel */ .gsconnect-tooltip { border-radius: 3px; min-width: 0; min-height: 0; padding: 6px; } .gsconnect-tooltip > StBoxLayout { spacing: 6px; } .gsconnect-tooltip StIcon { icon-size: 16px; } .gsconnect-tooltip StLabel { font-weight: normal; text-align: left; } .gsconnect-tooltip StLabel:rtl { text-align: right; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/utils/�������������������������������������������������������0000775�0000000�0000000�00000000000�14215434441�0021436�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/src/utils/remote.js����������������������������������������������0000664�0000000�0000000�00000034031�14215434441�0023270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const SERVICE_NAME = 'org.gnome.Shell.Extensions.GSConnect'; const SERVICE_PATH = '/org/gnome/Shell/Extensions/GSConnect'; const DEVICE_NAME = 'org.gnome.Shell.Extensions.GSConnect.Device'; const DEVICE_PATH = '/org/gnome/Shell/Extensions/GSConnect/Device'; const _PROPERTIES = { 'Connected': 'connected', 'EncryptionInfo': 'encryption-info', 'IconName': 'icon-name', 'Id': 'id', 'Name': 'name', 'Paired': 'paired', 'Type': 'type', }; function _proxyInit(proxy, cancellable = null) { if (proxy.__initialized !== undefined) return Promise.resolve(); return new Promise((resolve, reject) => { proxy.init_async( GLib.PRIORITY_DEFAULT, cancellable, (proxy, res) => { try { proxy.init_finish(res); proxy.__initialized = true; resolve(); } catch (e) { Gio.DBusError.strip_remote_error(e); reject(e); } } ); }); } /** * A simple proxy wrapper for devices exported over DBus. */ var Device = GObject.registerClass({ GTypeName: 'GSConnectRemoteDevice', Implements: [Gio.DBusInterface], Properties: { 'connected': GObject.ParamSpec.boolean( 'connected', 'Connected', 'Whether the device is connected', GObject.ParamFlags.READABLE, null ), 'encryption-info': GObject.ParamSpec.string( 'encryption-info', 'Encryption Info', 'A formatted string with the local and remote fingerprints', GObject.ParamFlags.READABLE, null ), 'icon-name': GObject.ParamSpec.string( 'icon-name', 'Icon Name', 'Icon name representing the device', GObject.ParamFlags.READABLE, null ), 'id': GObject.ParamSpec.string( 'id', 'deviceId', 'The device hostname or other unique id', GObject.ParamFlags.READABLE, '' ), 'name': GObject.ParamSpec.string( 'name', 'deviceName', 'The device name', GObject.ParamFlags.READABLE, null ), 'paired': GObject.ParamSpec.boolean( 'paired', 'Paired', 'Whether the device is paired', GObject.ParamFlags.READABLE, null ), 'type': GObject.ParamSpec.string( 'type', 'deviceType', 'The device type', GObject.ParamFlags.READABLE, null ), }, }, class Device extends Gio.DBusProxy { _init(service, object_path) { this._service = service; super._init({ g_connection: service.g_connection, g_name: SERVICE_NAME, g_object_path: object_path, g_interface_name: DEVICE_NAME, }); } vfunc_g_properties_changed(changed, invalidated) { try { for (const name in changed.deepUnpack()) this.notify(_PROPERTIES[name]); } catch (e) { logError(e); } } _get(name, fallback = null) { try { return this.get_cached_property(name).unpack(); } catch (e) { return fallback; } } get connected() { return this._get('Connected', false); } get encryption_info() { return this._get('EncryptionInfo', ''); } get icon_name() { return this._get('IconName', 'computer'); } get id() { return this._get('Id', '0'); } get name() { return this._get('Name', 'Unknown'); } get paired() { return this._get('Paired', false); } get service() { return this._service; } get type() { return this._get('Type', 'desktop'); } async start() { try { await _proxyInit(this); // For GActions & GMenu we pass the service's name owner to avoid // any mixup with instances. this.action_group = Gio.DBusActionGroup.get( this.g_connection, this.service.g_name_owner, this.g_object_path ); this.menu = Gio.DBusMenuModel.get( this.g_connection, this.service.g_name_owner, this.g_object_path ); // Poke the GMenu to ensure it's ready for us await new Promise((resolve, reject) => { this.g_connection.call( SERVICE_NAME, this.g_object_path, 'org.gtk.Menus', 'Start', new GLib.Variant('(au)', [[0]]), null, Gio.DBusCallFlags.NONE, -1, null, (proxy, res) => { try { resolve(proxy.call_finish(res)); } catch (e) { Gio.DBusError.strip_remote_error(e); reject(e); } } ); }); } catch (e) { this.destroy(); throw e; } } destroy() { GObject.signal_handlers_destroy(this); } }); /** * A simple proxy wrapper for the GSConnect service. */ var Service = GObject.registerClass({ GTypeName: 'GSConnectRemoteService', Implements: [Gio.DBusInterface], Properties: { 'active': GObject.ParamSpec.boolean( 'active', 'Active', 'Whether the service is active', GObject.ParamFlags.READABLE, false ), }, Signals: { 'device-added': { flags: GObject.SignalFlags.RUN_FIRST, param_types: [Device.$gtype], }, 'device-removed': { flags: GObject.SignalFlags.RUN_FIRST, param_types: [Device.$gtype], }, }, }, class Service extends Gio.DBusProxy { _init() { super._init({ g_bus_type: Gio.BusType.SESSION, g_name: SERVICE_NAME, g_object_path: SERVICE_PATH, g_interface_name: 'org.freedesktop.DBus.ObjectManager', g_flags: Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION, }); this._active = false; this._devices = new Map(); this._starting = false; // Watch the service this._nameOwnerChangedId = this.connect( 'notify::g-name-owner', this._onNameOwnerChanged.bind(this) ); } get active() { return this._active; } get devices() { return Array.from(this._devices.values()); } vfunc_g_signal(sender_name, signal_name, parameters) { try { // Don't emit signals until the ObjectManager has started if (!this.active) return; parameters = parameters.deepUnpack(); switch (true) { case (signal_name === 'InterfacesAdded'): this._onInterfacesAdded(...parameters); break; case (signal_name === 'InterfacesRemoved'): this._onInterfacesRemoved(...parameters); break; } } catch (e) { logError(e); } } /** * org.freedesktop.DBus.ObjectManager.InterfacesAdded * * @param {string} object_path - Path interfaces have been added to * @param {Object} interfaces - A dictionary of interface objects */ async _onInterfacesAdded(object_path, interfaces) { try { // An empty list means only the object has been added if (Object.values(interfaces).length === 0) return; // Skip existing proxies if (this._devices.has(object_path)) return; // Create a proxy const device = new Device(this, object_path); await device.start(); // Hold the proxy and emit ::device-added this._devices.set(object_path, device); this.emit('device-added', device); } catch (e) { logError(e, object_path); } } /** * org.freedesktop.DBus.ObjectManager.InterfacesRemoved * * @param {string} object_path - Path interfaces have been removed from * @param {string[]} interfaces - List of interface names removed */ _onInterfacesRemoved(object_path, interfaces) { try { // An empty interface list means the object is being removed if (interfaces.length === 0) return; // Get the proxy const device = this._devices.get(object_path); if (device === undefined) return; // Release the proxy and emit ::device-removed this._devices.delete(object_path); this.emit('device-removed', device); // Destroy the device and force disposal device.destroy(); } catch (e) { logError(e, object_path); } } async _addDevices() { const objects = await new Promise((resolve, reject) => { this.call( 'GetManagedObjects', null, Gio.DBusCallFlags.NONE, -1, null, (proxy, res) => { try { const variant = proxy.call_finish(res); resolve(variant.deepUnpack()[0]); } catch (e) { Gio.DBusError.strip_remote_error(e); reject(e); } } ); }); for (const [object_path, object] of Object.entries(objects)) await this._onInterfacesAdded(object_path, object); } _clearDevices() { for (const [object_path, device] of this._devices) { this._devices.delete(object_path); this.emit('device-removed', device); device.destroy(); } } async _onNameOwnerChanged() { try { // If the service stopped, remove each device and mark it inactive if (this.g_name_owner === null) { this._clearDevices(); this._active = false; this.notify('active'); // If the service started, mark it active and add each device } else { this._active = true; this.notify('active'); await this._addDevices(); } } catch (e) { logError(e); } } /** * Reload all devices without affecting the remote service. This amounts to * removing and adding each device while emitting the appropriate signals. */ async reload() { try { if (this._starting === false) { this._starting = true; this._clearDevices(); await _proxyInit(this); await this._onNameOwnerChanged(); this._starting = false; } } catch (e) { this._starting = false; throw e; } } /** * Start the service */ async start() { try { if (this._starting === false && this.active === false) { this._starting = true; await _proxyInit(this); await this._onNameOwnerChanged(); // Activate the service if it's not already running if (!this.active) { await new Promise((resolve, reject) => { this.g_connection.call( SERVICE_NAME, SERVICE_PATH, 'org.freedesktop.Application', 'Activate', GLib.Variant.new('(a{sv})', [{}]), null, Gio.DBusCallFlags.NONE, -1, null, (proxy, res) => { try { resolve(proxy.call_finish(res)); } catch (e) { Gio.DBusError.strip_remote_error(e); reject(e); } } ); }); } this._starting = false; } } catch (e) { this._starting = false; throw e; } } /** * Stop the service */ stop() { if (this.active) this.activate_action('quit'); } activate_action(name, parameter = null) { try { const paramArray = []; if (parameter instanceof GLib.Variant) paramArray[0] = parameter; const connection = this.g_connection || Gio.DBus.session; connection.call( SERVICE_NAME, SERVICE_PATH, 'org.freedesktop.Application', 'ActivateAction', GLib.Variant.new('(sava{sv})', [name, paramArray, {}]), null, Gio.DBusCallFlags.NONE, -1, null, null ); } catch (e) { logError(e); } } destroy() { if (this._nameOwnerChangedId > 0) { this.disconnect(this._nameOwnerChangedId); this._nameOwnerChangedId = 0; this._clearDevices(); this._active = false; GObject.signal_handlers_destroy(this); } } }); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/����������������������������������������������������0000775�0000000�0000000�00000000000�14215434441�0022221�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/.editorconfig���������������������������������������0000664�0000000�0000000�00000000264�14215434441�0024700�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Manifests: 2-space indents [manifest.*.json] indent_style = space indent_size = 2 # l10n message files: 4-space indents [_locales/**.json] indent_style = space indent_size = 4 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/.eslintrc.json��������������������������������������0000664�0000000�0000000�00000000513�14215434441�0025014�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "env": { "browser": true, "webextensions": true }, "ignorePatterns": ["browser-polyfill.*"], "rules": { "no-console": [ "error", { "allow": [ "warn", "error" ] } ] } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/�������������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024002�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/ar/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024404�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/ar/messages.json���������������������������0000664�0000000�0000000�00000003063�14215434441�0027110�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Share links with GSConnect, direct to the browser or by SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "Send To Mobile Device", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "الخدمة غير Ł…ŲŖŲ§Ų­Ų©", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "لم ŁŠŲŖŁ… Ų§Ł„Ų¹Ų«ŁˆŲ± على أي جهاز", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "فتح في المتصفح", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "Send SMS", "description": "Context Menu label for sharing a link in an SMS message" } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/be/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024370�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/be/messages.json���������������������������0000664�0000000�0000000�00000003232�14215434441�0027072�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "ŠŠ±Š°Š³ŃƒŠ»ŃŒŠ²Š°Š¹Ń†Šµ спасылкі Š· GSConnect, Š½Š°ŠæŃ€Š°Š¼ŃƒŃŽ ў Š±Ń€Š°ŃžŠ·ŠµŃ€ або праз SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "ŠŠ“ŠæŃ€Š°Š²Ń–Ń†ŃŒ на Š¼Š°Š±Ń–Š»ŃŒŠ½ŃƒŃŽ ŠæŃ€Ń‹Š»Š°Š“Ńƒ", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Š”ŃŃ€Š²Ń–Ń Š½ŠµŠ“Š°ŃŃ‚ŃƒŠæŠ½Ń‹", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "ŠŠµ знойГзена прылаГ", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "ŠŠ“ŠŗŃ€Ń‹Ń†ŃŒ у Š±Ń€Š°ŃžŠ·ŠµŃ€Ń‹", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "ŠŠ“ŠæŃ€Š°Š²Ń–Ń†ŃŒ SMS", "description": "Context Menu label for sharing a link in an SMS message" } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/ca/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024365�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/ca/messages.json���������������������������0000664�0000000�0000000�00000003067�14215434441�0027075�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Compartiu enllaƧos amb el GSConnect, directament al navegador o mitjanƧant SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "Envia al dispositiu mòbil", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "El servei no estĆ  disponible", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "No s'ha trobat cap dispositiu", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "Obre al navegador", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "Envia un SMS", "description": "Context Menu label for sharing a link in an SMS message" } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/cs/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024407�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/cs/messages.json���������������������������0000664�0000000�0000000�00000003101�14215434441�0027104�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "SdĆ­let odkazy pomocĆ­ GSConnect, přímo do prohlížeče nebo prostřednictvĆ­m SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "Odeslat do mobilnĆ­ho zařízenĆ­", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Služba nedostupnĆ”", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "Nenalezeno žÔdnĆ© zařízenĆ­", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "Otevřít v prohlížeči", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "Poslat SMS", "description": "Context Menu label for sharing a link in an SMS message" } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/da/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024366�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/da/messages.json���������������������������0000664�0000000�0000000�00000003015�14215434441�0027067�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Dele links med GSConnect, direkte til browseren eller via SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "Send til Mobil Enhed", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Tjenesten er ikke tilgƦngelig", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "Ingen Enhed Fundet", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "ƅbn i browser", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "Send SMS", "description": "Context Menu label for sharing a link in an SMS message" } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/de/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024372�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/de/messages.json���������������������������0000664�0000000�0000000�00000003025�14215434441�0027074�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Teilen Sie Links mit GSConnect, direkt mit dem Browser oder per SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "An MobilgerƤt senden", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Dienst nicht verfügbar", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "Kein GerƤt gefunden", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "Im Browser ƶffnen", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "SMS senden", "description": "Context Menu label for sharing a link in an SMS message" } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/en/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024404�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/en/messages.json���������������������������0000664�0000000�0000000�00000002777�14215434441�0027123�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Share links with GSConnect, direct to the browser or by SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "Send To Mobile Device", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Service Unavailable", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "No Device Found", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "Open in Browser", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "Send SMS", "description": "Context Menu label for sharing a link in an SMS message" } } �gnome-shell-extension-gsconnect-50/webextension/_locales/es/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024411�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/es/messages.json���������������������������0000664�0000000�0000000�00000003065�14215434441�0027117�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Comparta enlaces con GSConnect, directamente al navegador o a travĆ©s de SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "Enviar a dispositivo móvil", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Servicio no disponible", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "No se encontró ningĆŗn dispositivo", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "Abrir en el navegador", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "Enviar SMS", "description": "Context Menu label for sharing a link in an SMS message" } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/et/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024412�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/et/messages.json���������������������������0000664�0000000�0000000�00000002775�14215434441�0027127�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Jaga linke GSConnectiga, otse brauserisse vƵi SMSi teel.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "Saada mobiilseadmesse", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Teenus pole saadaval", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "Seadet ei leitud", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "Ava brauseris", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "Saada SMS", "description": "Context Menu label for sharing a link in an SMS message" } } ���gnome-shell-extension-gsconnect-50/webextension/_locales/fr/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024411�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/fr/messages.json���������������������������0000664�0000000�0000000�00000003061�14215434441�0027113�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Partagez des liens avec GSConnect, directement vers le navigateur ou par SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "Envoyer vers l'appareil mobile", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Service indisponible", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "Aucun appareil trouvĆ©", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "Ouvrir dans le navigateur", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "Envoyer un SMS", "description": "Context Menu label for sharing a link in an SMS message" } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/gl/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024404�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/gl/messages.json���������������������������0000664�0000000�0000000�00000003047�14215434441�0027112�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Compartir ligazóns con GSConnect, directamente co navegador ou por SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "Enviar a dispositivo móbil", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Servizo non dispoƱƭbel", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "Non se atopou o dispositivo", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "Abrir no navegador", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "Enviar SMS", "description": "Context Menu label for sharing a link in an SMS message" } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/hu/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024416�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/hu/messages.json���������������������������0000664�0000000�0000000�00000003046�14215434441�0027123�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Share links with GSConnect, direct to the browser or by SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "KüldĆ©s mobil eszkƶzre", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "A szolgĆ”ltatĆ”s nem Ć©rhető el", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "Nem talĆ”lható eszkƶz", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "MegnyitĆ”s bƶngĆ©szőben", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "SMS küldĆ©se", "description": "Context Menu label for sharing a link in an SMS message" } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/it/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024416�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/it/messages.json���������������������������0000664�0000000�0000000�00000003041�14215434441�0027116�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Condividi collegamenti con GSConnect, direttamente nel browser o via SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "Invia al dispositivo mobile", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Servizio non disponibile", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "Dispositivo non trovato", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "Apri nel browser", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "Invia SMS", "description": "Context Menu label for sharing a link in an SMS message" } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/lt/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024421�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/lt/messages.json���������������������������0000664�0000000�0000000�00000003070�14215434441�0027123�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Bendrinti nuorodas, naudojant GSConnect, tiesiogiai ÄÆ narÅ”yklę ar per SMS žinutę.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "Siųsti ÄÆ mobilųjÄÆ ÄÆrenginÄÆ", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Paslauga neprieinama", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "Nerasta jokių ÄÆrenginių", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "Atverti narÅ”yklėje", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "Siųsti SMS", "description": "Context Menu label for sharing a link in an SMS message" } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/nl-BE/�������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024677�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/nl-BE/messages.json������������������������0000664�0000000�0000000�00000003004�14215434441�0027376�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Deel links met GSConnect, direct naar de browser of per SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "Verzend naar GSM", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Dienst onbeschikbaar", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "Geen toestel gevonden", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "Open in browser", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "Verzend SMS", "description": "Context Menu label for sharing a link in an SMS message" } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/nl-NL/�������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024722�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/nl-NL/messages.json������������������������0000664�0000000�0000000�00000003032�14215434441�0027422�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Deel links met GSConnect, direct naar de browser of via sms.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "Versturen naar mobiel apparaat", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Dienst niet beschikbaar", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "Geen apparaat gevonden", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "Openen in browser", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "SMS versturen", "description": "Context Menu label for sharing a link in an SMS message" } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/pl/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024415�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/pl/messages.json���������������������������0000664�0000000�0000000�00000003136�14215434441�0027122�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Udostępnianie odnośników za pomocą GSConnect, bezpośrednio do przeglądarki lub przez wiadomość SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "Wyślij na urządzenie mobilne", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Usługa jest niedostępna", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "Nie odnaleziono żadnego urządzenia", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "Otwórz wĀ przeglądarce", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "Wyślij SMS", "description": "Context Menu label for sharing a link in an SMS message" } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/pt-BR/�������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024726�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/pt-BR/messages.json������������������������0000664�0000000�0000000�00000003051�14215434441�0027427�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Compartilhe links com o GSConnect, diretamente no navegador ou por SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "Enviar para dispositivo móvel", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "ServiƧo indisponĆ­vel", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "Nenhum dispositivo encontrado", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "Abrir no navegador", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "Enviar SMS", "description": "Context Menu label for sharing a link in an SMS message" } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/ru/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024430�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/ru/messages.json���������������������������0000664�0000000�0000000�00000003160�14215434441�0027132�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "ŠžŃ‚ŠæŃ€Š°Š²Š»ŃŃ‚ŃŒ ссылки в веб Š±Ń€Š°ŃƒŠ·ŠµŃ€ или по SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "ŠžŃ‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ на ŃƒŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Дервис Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠµŠ½", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "Устройства не найГены", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "ŠžŃ‚ŠŗŃ€Ń‹Ń‚ŃŒ в Š±Ń€Š°ŃƒŠ·ŠµŃ€Šµ", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "ŠžŃ‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ ДМД", "description": "Context Menu label for sharing a link in an SMS message" } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/sk/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024417�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/sk/messages.json���������������������������0000664�0000000�0000000�00000003076�14215434441�0027127�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Zdieľanie odkazov s aplikĆ”ciou GSConnect, priamo cez prehliadač alebo formou SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "OdoslaÅ„ do mobilnĆ©ho zariadenia", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Služba nedostupnĆ”", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "NenaÅ”lo sa žiadne zariadenie", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "OtvoriÅ„ v prehliadači", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "OdoslaÅ„ SMS", "description": "Context Menu label for sharing a link in an SMS message" } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/sr-Latn/�����������������������������������0000775�0000000�0000000�00000000000�14215434441�0025322�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/sr-Latn/messages.json����������������������0000664�0000000�0000000�00000003025�14215434441�0030024�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Deli veze GSKonektom, direktno u pregldač ili putem SMS-a.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "PoÅ”alji na mobilni uređaj", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Servis nije dostupan", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "Nema nađenih uređaja", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "Otvori u pregledaču", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "PoÅ”alji SMS", "description": "Context Menu label for sharing a link in an SMS message" } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/sr/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024426�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/sr/messages.json���������������������������0000664�0000000�0000000�00000003216�14215434441�0027132�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "Дели везе Š“Š”ŠšŠ¾Š½ŠµŠŗŃ‚Š¾Š¼, Гиректно у преглГач или ŠæŃƒŃ‚ем ДМД-а.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "ŠŸŠ¾ŃˆŠ°Ń™Šø на мобилни ŃƒŃ€ŠµŃ’Š°Ń˜", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Дервис није Š“Š¾ŃŃ‚ŃƒŠæŠ°Š½", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "ŠŠµŠ¼Š° нађених ŃƒŃ€ŠµŃ’Š°Ń˜Š°", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "ŠžŃ‚Š²Š¾Ń€Šø у ŠæŃ€ŠµŠ³Š»ŠµŠ“Š°Ń‡Ńƒ", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "ŠŸŠ¾ŃˆŠ°Ń™Šø ДМД", "description": "Context Menu label for sharing a link in an SMS message" } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/tr/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024427�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/tr/messages.json���������������������������0000664�0000000�0000000�00000003021�14215434441�0027125�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "GSConnect ile bağlantıları doğrudan tarayıcı veya SMS ile paylaş.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "Mobil Cihaza Gƶnder", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Servis Mevcut Değil", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "Cihaz Bulunamadı", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "Tarayıcıda AƧ", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "SMS Gƶnder", "description": "Context Menu label for sharing a link in an SMS message" } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/uk/����������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024421�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/uk/messages.json���������������������������0000664�0000000�0000000�00000003277�14215434441�0027134�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "ŠŸŠ¾ŃˆŠøŃ€ŃŽŠ¹Ń‚Šµ ŠæŠ¾ŃŠøŠ»Š°Š½Š½Ń за Š“Š¾ŠæŠ¾Š¼Š¾Š³Š¾ŃŽ GSConnect, Š½Š°ŠæŃ€ŃŠ¼Ńƒ Го Š±Ń€Š°ŃƒŠ·ŠµŃ€Š° або через SMS.", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "ВіГправити Го Š¼Š¾Š±Ń–Š»ŃŒŠ½Š¾Š³Š¾ ŠæŃ€ŠøŃŃ‚Ń€Š¾ŃŽ", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "Дервіс Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½ŠøŠ¹", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "ŠŸŃ€ŠøŃŃ‚Ń€Š¾Ń—Š² не знайГено", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "ВіГкрити у Š±Ń€Š°ŃƒŠ·ŠµŃ€Ń–", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "ВіГправити SMS", "description": "Context Menu label for sharing a link in an SMS message" } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/zh-CN/�������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024721�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/zh-CN/messages.json������������������������0000664�0000000�0000000�00000003012�14215434441�0027417�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "é€ščæ‡ GSConnect ē›“ęŽ„å°†é“¾ęŽ„å‘é€åˆ°ęµč§ˆå™Øęˆ–ēŸ­äæ”ć€‚", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "å‘é€åˆ°ē§»åŠØč®¾å¤‡", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "ęœåŠ”äøåÆē”Ø", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "ę²”ęœ‰ę‰¾åˆ°č®¾å¤‡", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "åœØęµč§ˆå™Øäø­ę‰“å¼€", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "å‘é€ēŸ­äæ”", "description": "Context Menu label for sharing a link in an SMS message" } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/zh-TW/�������������������������������������0000775�0000000�0000000�00000000000�14215434441�0024753�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/_locales/zh-TW/messages.json������������������������0000664�0000000�0000000�00000002776�14215434441�0027471�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "extensionName": { "message": "GSConnect", "description": "The extension name" }, "extensionDescription": { "message": "ē›“ęŽ„ē”Øē€č¦½å™Øęˆ–ē¶“ē”±ē°”čØŠå’Œ GSConnect å…±äŗ«éˆēµ", "description": "The extension description" }, "contextMenuMultipleDevices": { "message": "å‚³é€åˆ°ę‰‹ę©Ÿ", "description": "Top-level Context Menu label when multiple devices are available" }, "contextMenuSinglePlugin": { "message": "$NAME$ ($PLUGIN$)", "description": "Context Menu label for devices that only support a single plugin", "placeholders": { "name": { "content": "$1", "example": "Nexus 4" }, "plugin": { "content": "$2", "example": "Open in Browser" } } }, "popupMenuDisconnected": { "message": "ęœå‹™ē„”ę³•ä½æē”Ø", "description": "Popup Menu label when disconnected from the GNOME Shell extension" }, "popupMenuNoDevices": { "message": "ę‰¾äøåˆ°č£ē½®", "description": "Popup Menu label when no devices are connected or have supported plugins enabled" }, "shareMessage": { "message": "ä»„ē€č¦½å™Øé–‹å•Ÿ", "description": "Context Menu label for opening a link in the device's web browser" }, "smsMessage": { "message": "ē™¼é€ē°”čØŠ", "description": "Context Menu label for sharing a link in an SMS message" } } ��gnome-shell-extension-gsconnect-50/webextension/background.html�������������������������������������0000664�0000000�0000000�00000000364�14215434441�0025231�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> </head> <body> <script src="js/browser-polyfill.min.js"></script> <script src="js/background.js"></script> </body> </html> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/gettext.js������������������������������������������0000775�0000000�0000000�00000007016�14215434441�0024252�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env gjs 'use strict'; const ByteArray = imports.byteArray; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; // eslint-disable-next-line no-redeclare const _ = (msgid) => GLib.dgettext('org.gnome.Shell.Extensions.GSConnect', msgid); // POT Patterns const MSGID_REGEX = /^msgid "(.+)"$/; const MSGSTR_REGEX = /^msgstr "(.+)"$/; // MSGID -> MESSAGE Reverse Map const MSGID = { 'GSConnect': 'extensionName', 'Share links with GSConnect, direct to the browser or by SMS.': 'extensionDescription', 'Send To Mobile Device': 'contextMenuMultipleDevices', 'Service Unavailable': 'popupMenuDisconnected', 'No Device Found': 'popupMenuNoDevices', 'Open in Browser': 'shareMessage', 'Send SMS': 'smsMessage', }; // TRANSLATORS: Extension name _('GSConnect'); // TRANSLATORS: Chrome/Firefox WebExtension description _('Share links with GSConnect, direct to the browser or by SMS.'); // TRANSLATORS: Top-level context menu item for GSConnect _('Send To Mobile Device'); // TRANSLATORS: WebExtension can't connect to GSConnect _('Service Unavailable'); // TRANSLATORS: No devices are known or available _('No Device Found'); // TRANSLATORS: Open URL with the device's browser _('Open in Browser'); // TRANSLATORS: Share URL by SMS _('Send SMS'); // JSON.load = function (gfile) { try { const data = gfile.load_contents(null)[1]; if (data instanceof Uint8Array) return JSON.parse(ByteArray.toString(data)); else return JSON.parse(data.toString()); } catch (e) { logError(e); return {}; } }; // Find the cwd, locale dir and po dir const cwd = Gio.File.new_for_path('.'); const localedir = cwd.get_child('_locales'); const podir = cwd.get_parent().get_child('po'); // Load the english translation as a template let template = localedir.get_child('en').get_child('messages.json'); template = JSON.load(template); // let info; const iter = podir.enumerate_children('standard::name', 0, null); while ((info = iter.next_file(null))) { const [lang, ext] = info.get_name().split('.'); // Only process PO files if (ext !== 'po') continue; /** * Convert glibc language codes * * pt_BR => pt-BR * sr@latin => sr-Latn */ let langCode = lang.replace('_', '-'); langCode = langCode.replace('@latin', '-Latn'); print(`Processing ${lang} as ${langCode}`); // Make a new dir and file const jsondir = localedir.get_child(langCode); const jsonfile = jsondir.get_child('messages.json'); GLib.mkdir_with_parents(jsondir.get_path(), 448); // If the translation exists, update the template with its messages let json = JSON.parse(JSON.stringify(template)); if (jsonfile.query_exists(null)) json = Object.assign(json, JSON.load(jsonfile)); // Read the PO file and search the msgid's for our strings let msgid = false; let po = iter.get_child(info).load_contents(null)[1]; po = ByteArray.toString(po); for (const line of po.split('\n')) { // If we have a msgid, we're expecting a msgstr if (msgid) { if (MSGSTR_REGEX.test(line)) json[msgid]['message'] = line.match(MSGSTR_REGEX)[1]; msgid = false; // Otherwise set msgid to a mapped message } else if (MSGID_REGEX.test(line)) { msgid = MSGID[line.match(MSGID_REGEX)[1]]; } } // Write the (updated) translation json = `${JSON.stringify(json, null, 4)}\n\n`; jsonfile.replace_contents(json, null, false, 0, null); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/images/���������������������������������������������0000775�0000000�0000000�00000000000�14215434441�0023466�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/images/desktop.svg����������������������������������0000664�0000000�0000000�00000000422�14215434441�0025656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<svg fill="#000000" height="18" viewBox="0 0 24 24" width="18" xmlns="http://www.w3.org/2000/svg"> <path d="M0 0h24v24H0z" fill="none"/> <path d="M21 2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h7v2H8v2h8v-2h-2v-2h7c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H3V4h18v12z"/> </svg>����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/images/gsconnect-128.png����������������������������0000664�0000000�0000000�00000002500�14215434441�0026464�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���€���€���Ć>aĖ���sBIT|dˆ��� pHYs��;��;̶”ƒ���tEXtSoftware�www.inkscape.org›ī<��½IDATxœķŻ=oEšgf—;Cpä!Č•++Jņ–,hų4H4A`Žæ¢BJ$“‡y))\CĖ…-ErA¶„P¤Ä–EŖ ōĻī½ĶzvgvęłUŃ9{÷_Ļs{÷ģŽÉ�ÅF5ŁhżĶõWt‰/�¼ `ŁķHdéĄ6J=Ē‡¶[ą|ń°b»-µź!J}Ż6ŚöQΟł\üš¬@—ŸŁnd�üwŲ§0ݰݠI�ųš®Ė¶4 �E„HøÜõ~øłė»ģ-­5ƒ”Óū|tÓéżńŠ¢,sžürŽh‰R ™Ī|1Š’,ĖžhļŠ’, ’Ł0�­Č² Jõąé yŽü=Į�8¦µ†ŅżłµögŅžčCõ“�‡śRż$Ą”¾T?‰pØ/ÕOb�éSõ“�GśTż$Ą¾U?©ŸS¦oÕOźļ䁘TżŽŽŽqūNƒƒūųēńćNēY]»f*7*`Ū ķļīÖ>2Ī#ĄU«ßŃŃ1Ž»y ;÷ö:_ü)– š [][»Zż!pAÕźwūN³³3OÓĢ“dµļ 0�0©śÜ÷4Ķ|¦ö½¾ø€IÕÆzŲæńö×]ƒA®ńĀå§?ƒųķ­·ž’·šš½ ±ś]Z²>‡µ=Zõ˓³ūSŃ @!^õ{n˜7ŗÅ�4ŚU?ąŅ°Y €B»ź·4Ȑéf‰d�,…xÕļłožž`�,…vÕok ņęĖČ�Xˆ„śIaķMąb©~° ˜ŖŸÄ�,(¦ź'1� Š©śI Ąb«~°€ŲŖŸÄ�Ģcõ“Āڳ�ÅXż$`†X«ŸÄ�Ģkõ“€b­~0EĢÕOb�¦ˆ¹śI Ą±W?)¬½ DģÕOb�*RØ~P‘Bõ“€ŠŖŸÄ�©T?‰R©~p.„ź'…µĒ„Tż$�éU?‰@zÕOb�^õ“’@ŠÕOJ>�)V?ÉłŽ¼ł‰ė»LJÕOJž’®ŖŸÄ�¤«ź'1�č²śIMpź| ź“śIM°ķ| ź“śIö(õĄC÷£¤«ėź'Y?źx<>D©ÆĆØœ“0Srŗ®~’Õ#žå§›J᣶†‰ĮįļXżÕOZųĄÅo‡ź'-�.~;|U?in�øųķńUż¤™ąā·ĖWõ“¦€‹ß.ŸÕOš8æ}>«ŸT �æ}¾«ŸT‹įŽŽŽ;>I‰ļź'Õ_”©żqAr'„ź'Õ`Jlł$!T?©€A¾ō €æ=Ģ’„ŖŸ41ŠėFį»i?§élÆtmwē©5X‹b«0ƌ�T’1Efź™ˆļ‹¾Ro€/Q›y*Ŗ(¶Šg²įU„ŌĄ=�ŗ‹ŗĀ×xĒV×®�Xö=ĒöwwVä žOFGFü™IcšKõ6Ą1ƒ2ŌĻL>P¹z·zc8§¤"ń×ńńƒ_~é'cŌ(\QĄpžVķ1Ą ~V¹z}’īŻ?}ĪBDDDDDDDDDDDDDDDDDDDķłD«°Aųź>Ō����IEND®B`‚������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/images/gsconnect-16.png�����������������������������0000664�0000000�0000000�00000000641�14215434441�0026404�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������ó’a���sBIT|dˆ��� pHYs��b��b8z™Ū���tEXtSoftware�www.inkscape.org›ī<��IDAT8Å1KBa†ŸóŪÕ*‡Ą””­A"¤ !mlhŠ ØßPōZė74Қw)½Ž )47«$9ź½ vE»·!z·ļ÷łĪ9šß€£ćĆ3A®~S ĻĖ·wץˆÉŠ»÷ 3 Ęg€ŗ_§rļe�LŅUķT>NDDš¼[ļ#–Īn_näß:‰Ŗ–~Ÿµ­ƒ/!°²čŅōĖ™D+Øjäķ:ŠÜ@U‘[pĒƒĒ ģÄńkpģøf‚0čŌŖ>µŖ?SŌīöŲ<ŁgŽ m©TZÅ /ā&yi½¢FH9ć{¤Ó©%Ūh<­‡B6NŠīö<Wč¤Ü ‡ƒeł”I.æ“3fxśŽ$ķ’]>Łm?a&J̧����IEND®B`‚�����������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/images/gsconnect-22.png�����������������������������0000664�0000000�0000000�00000001101�14215434441�0026371�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������Ä“l;���sBIT|dˆ��� pHYs�� &�� &Q©©3���tEXtSoftware�www.inkscape.org›ī<��¾IDAT8Ż”1O1†ßĻžöĮ%Q:B”-H…"Xå0’”? ‰©CF$:”Že$„XŹ€€t×r@/ ¢˜”Mr§»Jw0Į;ŁÆķǟ_Łž›ØŪ(•JSD‰§ĄŒĒ•Jå�øē óÕ�£OƒĢ9€±0Ęz·¼„™·3ÉDŠJ÷śµļ5lo±ŗ}NÖZ#ŸĖ%33˜śk-+4.QbD$ąŗæą8.ĒÅÕÕ5Zķ¶˜]TœTRJl~śŒ}œQż* ÓŁ坵¹µüH0£Ł¼Įäü ”]čł¶fxG?pz°WH‘€ŃeĄ \²“`f뫌‹Ž³H&¤ˆOoH…żT`É2šVūb)0ČaThĻ»Dżųäæ`­4č_¾¾ß^żõ‡U4ž Ų«īVóÕŻj¢źŻß—(N¬@Ag¢ń0�ll¬Æ2¢ƒĪi"*€o{ūy�ö’qń”ĖåāŃI}Œ©'…€ķæģąė*—µĒ¹ŃhŚYėĀ�siĄw­–]ŪśˆŸ:üGÜŻžļ7Ļc‘LŅ…7lš>:B÷mĖ}z�»ós »µ����IEND®B`‚���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/images/gsconnect-24.png�����������������������������0000664�0000000�0000000�00000001125�14215434441�0026401�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������ąw=ų���sBIT|dˆ��� pHYs�� �� �šœ���tEXtSoftware�www.inkscape.org›ī<��ŅIDATH‰ķ”=oÓP†Ÿė{c;JR”ŠBH-R Ŗ"ŚXŲ:PFf„Äo°€„ā/�RĒ"!13˜Æ”Biš,H¤Ž(…Ś$Ā6 ±ģʤµaƒgņ9ēś}ļ¹>¾šŸć‡[­›«Ąæ#­>y¼v@%ņsµzFc!—˜®ėH)ćŲq^3pŻłqœ4`¦^ćźµ+¹ ĆDˆų čln1pŻ8Ör©ķCJ™ĻBM­DėėĻŽFqŖŻéŃģœYXlŽ ¾{ hšF»ÓåŁĖ'ę–ć¼5š —Jg·ŪÆ~p^¼)l „" BĢźQfNžOÕf«žŪC” }!R“™5]ič ŁBRŖÄ”¦b¦„ Aöī„&(ėéZnƒi£ij¢±ÜJfĻ…�*ĘdgÉÕA·Ūć¾żąŠf¾?Dŗ €©K¤6Ł™°mŪ ½÷<?×ŲöŻĻõ]øpÄĢ~Uٶ­–z*(­XV%>a(Ųõ&GsL¹\6ŌĘĘŪcA¤®ēRž…ļłQ=‡ēnŃ’ZJÕöv¶‰Ā &�Z­K!b%ŠņŻMßö¼ć? n’vA¤-Næ A£ŁlP۟—šåć8Ŗ’š^Xv™V \����IEND®B`‚�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/images/gsconnect-256.png����������������������������0000664�0000000�0000000�00000005304�14215434441�0026473�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������\rØf���sBIT|dˆ��� pHYs��v��v§Āxź���tEXtSoftware�www.inkscape.org›ī<�� AIDATxœķŻ_hwĒńļļybҤmŖo·›Žu†"±—s°¼Øv³›*E†E/†-n"" :‰‡00éY\ļā–fkń¢°?S*dė„!Ć,ͳsƔols¾ĻIžßļłżžēżŗŻŁž_Īńłä4ļœ*�����������������������������������������������,.ōĻ<yfĀuåœsņøˆ<$"‡CŸˆČ]Y‘+ŅĖf:Īrȋ€©©©‘£ć£/ā¾%"YØė é‰ČōęĘօ………ķ 2�SSS#GŽ¾.…ūJˆė‰»ŗ¹±õDˆņųčųč‹Üü@i}!༿8ó䙉¬'ŽöƒčfŅ;97÷Ś_}^ÄūM™÷Ü7C\h˜¼üœļ‹xæ1 )ó}  ‰ )Nū¾FˆļĢøŠDŽļp$Ą5€&:źūüŁh1�h1�h1�h1�h1�h1�h±”ŗ`łįÅgė>"•e™ Ō}Œ¾¾’Ģź>B_¼@²ņ<śļ_Ńc�$ēœäY^÷1’Ē� Iy>TĆ_h×< �’”ē|÷Æ€ääy.Īńķæ �’3Ä’*Ć� )Y–‰ĖųŸmUx&‘Ņ_µ�$ƒōW=�É żU@2HÕc�ҟ �’@śóƒ@ōHžš¬"z¤?�Dō瀨‘žüb�5ҟ_ �¢Eśó@“Hž1�ˆé/ žaD‰ō€čžĀa�Ņ_8 �¢Cś ‡@THa1�ˆ é/,�Ń ż…dzhžĀc�Ņ_=�DōWŽs! eÓßźūwdéĶkróęŠüsmM¶?Łö|²zMLž*Œ‡Ü‘U)Š+… Ķ,æw}y’>€Ś•I;;;2óņ+²“tMz½^ “%ᰈœēN8é~gā˧¦·>\æpūöķRĖČP;+żķģģČsĻ’\ßāęļ/“B¾=śŁĻżńųńć#åž FeŅßĢĖÆČŹ­æ:QśœøGĒŽ}ž…2e�P++ż­¾G––®:MƒøāüÄääĆÖĆ�Ō¦Lś[ZŗŹŪžżÉEÜ9ėA �jS&żŻ\¾ę0äN[`�P›2éomm-ĄIėAėd@Ō¢ģ§ž>1:’ćOOWu¤č eņ…ńž?Ģ’ŻsOõūĒG­kš�µąS¶Ć‡ü?G �‚ćS¶<s2:ģ’³¼ ŽOżŁĘF†‚|4‚@P|źĻęDäšH˜ēˆ@P|źĻvh8—< ó$1�ŠæšÓv$Ą’ž‹@0ü…Ÿ¶į”L†‡ĀŻ– �‚!żŁB¤?@¤?[Øō§ńŠ ҟ-TśÓ�xGś³…L�ļH¶éOc�ąéĻ2żi �¼"żŁB§?€W¤?[čō§1�š†ōg«#żi¼:š†ōg«#żi �¼ żŁźJ�/H¶ŗŅŸĘ�Ą ҟ­®ō§1�ØéĻVgśÓź?‡ōg«3żi �*Eś³Õž4^)TŠōg«;żi �*Cś³Åž4�•!żŁbH€Źžl1¤?@%H¶Xҟ×i,ҟ-–ō§1�80ҟ-¦ō§ńŖįĄH¶˜ŅŸĘ�ą@H¶ŲҟĘ�ą@H¶ŲҟĘ�ą@H¶ŲҟĘ�`ßH¶ӟļÉ=ҟ-Ęō§1�Ųҟ-Öō§ń b_H¶XӟĘ�``¤?[ĢéOc�00ҟ-ęō§1�éĻsśÓ� „ōg‹=żiiœŃ żŁbO€ŅH¶ҟƫ‰ŅH¶ҟĘ� ҟ-•ō§1�(…ōgK%żi �J!żŁRI�éĻ–RśÓŅ;1‚#żŁRJ€¾H¶ŌҟĘ+‹¾H¶ŌҟĘ�`O¤?[ŠéOc�°'ҟ-Åō§1�ŲéĻ–bśÓ�ÜéĻ–jśÓŅ>=¼!żŁRM€{žl)§?W÷ żŁRN€]H¶ŌӟĘ�`ҟ-õō§1�Ų…ōgK=żi �ž‡ōgkBśÓšó•ąĄH¶&¤?€ˆžŹhJśÓxÅ!"¤æ2š’ž4�¤æš”ž4�¤æš”ž4�¤æš”ž4 åH¶¦„?­™_J#żŁš–ž“čæ²_|¾ī# Åš˜ž4Ž�}41żi �°‡¦¦?�öŠŌō§1�Ąššž4�ø&§?­ł_!°MN�üŸ¦§?-Ä�løP™¦§?-Ä�|ą@%ڐž“°ą@%ڐž4’ŠĖfD¤ēż:@ڐž4ļŠét–EdŚ÷u€ƒjKśÓ‚|µ›[DäjˆkūՖō§€………ķĶ­'œ/‰H7Ä5A“)żiĮŽļ,,,l_šūĆәōN:qæ‘!"mJš×÷<?żŁO.:'?ņy 4Ūß’qĒū5Ś–ž4oļ�øł‘ж„?ĶĖ�pó#%mKZåĄĶ”“1żi•~åÜüHMӟVŁ�pó#5mMZ%ĄĶµ5żi�n~¤ØĶéO;Š�pó#UmNھ€›)ksśÓö5�ÜüHYŪӟ6š³ĄĶŌµ=żi �7?RGśŪ­ō�pó£ H»•�n~4éļ^ę�pó£)H÷2rćʍgBšōwÆ2Xó~ Ą3ŅßżŁĻHįn8ąéļžĢp™ü)ÄA�_H{3 čŗĖ!ųBśŪ›9�NgY\q=Äa€Ŗ‘žś+÷SW<ėł€¤æžJ @ē÷ƽ)"óžĻTŽō×_ég'wŻļu‹ü“ˆŒ{<0+æ:_÷’V:ŒĪĪ^^u…|CD>õx� ō›—.Ķæ."<@`’jTgnž—ā伈ģx8€€öõ»‘Łłßā¾*"’Ŗų<�Ś÷/Gæ:÷ź¹ėžź�¬}:bvöņjgnžė’õ旅€ōTśgĻžżbįŗ_s’="NNˆˆČ”*Ɓv ńŽd7ß}»ļ=ĪÆH!j“§>‘#uŸ#E…ČĘņ»oė÷> Ø…|P÷RåÄ~ī�DĶI±X÷RUˆ¼a=†@Ō š‘^ŻēHP7sÅo­1�ˆŚņ{חÅÉtŻēHłõ_ŽygÅz€čm}ø~”ājŻēHČ[Ÿ~|÷»eČߔ€č­ÆÆwĒĒĘf?32ö€8ł’šk/]'ņR÷ć»O­¬¬ü»Ģæ@DR&&'qēDÜiyHH„›"²źDÅ3eŽö����������������������������������������������������������ŠV’aßaS(ģu����IEND®B`‚����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/images/gsconnect-32.png�����������������������������0000664�0000000�0000000�00000000773�14215434441�0026410�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR��� ��� ���szzō���sBIT|dˆ��� pHYs��Ä��Ä•+���tEXtSoftware�www.inkscape.org›ī<��xIDATX…ķ–1NĆ@Dē’æZ‚čø�HDBĒ ¢ŃQpD"ˆq� :(ā”qŠ\¤J„ā ¦!hmƓM¼yŻŚcĻxżĒ2°äæCęāąp?°³`Ļšõå­>^pźä¢Ķ f.”MqŁjr`fhdŽ_œ7²ŚBN9ˆXŸĖŠ÷�Dał»�"*5Ś“qß+ē�‚F³ž`˜«©T·b€Ā^÷£ī5€ˆ€ˆŠ ±wöhÕhÅŲXšÜ>®ž_r¾r)©ń€™A<łvĀ„Po\Ŗ·ØĢ|z ąR=P²/\ŖWŅį¬ČS€éžµ’ż0®Ž$“bhe·*`žź%®·¼nŻĢŸ(…­z&é½9’`«žÉļÜŻß¶ˆ’? ó2}ČÆž ęW>ĢMņŖg¢� Š¢#ßę@~õ2b¢Šć'�›¾ĢßNt3ü:øS©nw€xŚ<uzŻĻŻEų/Y2ß’‚1”§D²Č����IEND®B`‚�����gnome-shell-extension-gsconnect-50/webextension/images/gsconnect-48.png�����������������������������0000664�0000000�0000000�00000001417�14215434441�0026413�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���0���0���Wł‡���sBIT|dˆ��� pHYs��%��%IR$š���tEXtSoftware�www.inkscape.org›ī<��ŒIDAThķ˜ĻkA†Ÿ™Łģ¦©=y…Õ‹é1-č”=H-…Ņ’„Š=xō?c!ŠÕC±?…[0zH{5kŃSkš/©l“M²›ĶĪÜēųķ~Ėū~3ó² ¤¤¤¤$‰h-LĻMߖ –ŃōtcWN•J„Ŗ·([ßgś —O<ĄØ™Öb›)„cFOxü“YŻ&& LLāSH)±ķĪ3ŪXß`łć§Īżqˆ ƒR]gŲ“D !PREśF¢”²|r0 ˆ6}HŠ€R !"ŽŸ)'–²ų^;äŁó—Ōj‡AŪīÜ_�ö®šŚŻŽ¬&b@J‰’ĶĶ2µŚ!#ćõå²™ŒÅĪŹŅØRī š"ēŃYÆ×É?ģŁ#€ėW³()ŲYY­Hą ōY[”dū™1n ßč¼’õß, ?}Ū’Ų–æT£śĪįÓƬ>ž{” ŁW͘óč KαŗcśłėĄ°ÓżĢ10ččōbÄĄ £Ó‹!ƒN/±ˆ#:½Än Žč¼šżn×V×Y[]- (?~žņ­÷ŠN/m+ąŗīiQQ ^Ś/¶“*UŸw$:½\ŲB‹‹‹7‘ĻĄČ …łQŁŻćčų÷…Zčōņošāæ`H¼A£Ó‹„Ė!‚G'@.7t š[h{»ü@ óā½)&:„Ö· iĄ²œwõ³Ó§n ZdŹpģ GCŁĄ='§ö i X,ĻĪ>¾ļjõ ” €Ńꃭ÷”{®{‘/ö¢q7ŸŸ×Z¼ī§W =_)—ß$j�cłüœDŒ…ir]¾U¾n½tLŗRRRRžžpˆ…¼ż(z����IEND®B`‚�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/images/gsconnect-64.png�����������������������������0000664�0000000�0000000�00000001417�14215434441�0026411�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���@���@���ŖiqŽ���sBIT|dˆ��� pHYs��‡��‡åńe���tEXtSoftware�www.inkscape.org›ī<��ŒIDATxœķš?Ó0‡ÆŅ‡Ų˜(ŒŒ7·#±±!!1±f@bDTüā Ś[€”K%֛ų » ĮÄĄŌ®‰ !tf€Fo”©c;¶ĪĻTÕéū±“_¬H$‰D"qT!]ƕ«—/AŃĄiż±ĀŒ€b<~½·ŖYhxHÄ>x� ōa kÖ ōœtØĪčōŽYŻļõļŗģG-„ČóN­coßŗSÆf“łFŹŚóU›h¤ÖėF#@ʬ"“͉H€żŁ" „‘ƒéG$27æĮ B€„»nŗSk‰EōMŽćłī Ģē_źlmļØ?g T|Ü»¾xō v_ž/zUī ‚^<ś¾Ģē�€‹×‡k×É3S'?A¾zx `{ƒ W€­čŪģźē9X¶¢O ĀF®¬�[Ńw¼“U>@)ĄVō€ĶNõe¤�[»¾n.!Eõeœ�›»¾7æĮ °µėĖ3<ū’šą>ś8A š}œ ųŠ>N0|FßŅyŸŃ>£„�ßŃĒ B€ļčć"ĄoōqZŠFōqZŠFō-æīś ŹūaŻč㓾l°nōqōf¦ņĶŗŃĒŃ  ˆA‚IōqVŖ{ņōqŸ÷«:ąÓēéŹļM¢óŗÆĆ4ś8Kb<`}œR@lƒo}œņšL&7WóH“čć”+€ Ę«y¤IōqŹ*Dz›?~~æ�ąœ•ŹŽxó¬°ZÆ\£Ńč[žuĻŌ€•æacĄĶ{'ŲŚŽ™ÖŽ^~Ųwˆh/ @`åÉtŖph÷:J$‰D"‘HDČ/—YÖ4’Ą����IEND®B`‚�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/images/gsconnect.svg��������������������������������0000664�0000000�0000000�00000004462�14215434441�0026200�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="32px" height="32px" viewBox="0 0 32 32" version="1.1" id="SVGRoot"> <defs id="defs6152" /> <metadata id="metadata6155"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:title></dc:title> </cc:Work> </rdf:RDF> </metadata> <g id="layer1" style="display:inline"> <rect rx="1" y="4" x="3" style="opacity:1;fill:#555753;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.57287949" height="18" width="26" id="rect3749" ry="1" /> <rect y="6" x="5" style="fill:#d3d7cf;fill-opacity:1;stroke:none;stroke-width:0.57592726" height="14" width="22" id="rect3751" /> <path d="M 3,22 1,24 H 31 L 29,22 Z" style="fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.32499966" id="path3755" /> <path d="m 1,24 h 30 c 0,1 -1,2 -2,2 H 3 C 2,26 1,25 1,24 Z" style="fill:#555753;fill-opacity:1;stroke:none;stroke-width:0.4976353" id="path3753" /> <path d="M 16,20 22,6 h 5 v 14" style="fill:#ffffff;fill-opacity:0.1882353;fill-rule:nonzero;stroke:none;stroke-width:0.63332039" id="path3701-7" /> </g> <g id="layer2" style="display:inline"> <rect rx="1" y="10" x="21" style="display:inline;opacity:1;fill:#2e3436;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.36212718" height="17" width="10" id="rect3749-3" /> <rect y="11" x="22" style="display:inline;fill:#729fcf;fill-opacity:1;stroke:none;stroke-width:0.37558597" height="14" width="8" id="rect3751-6" /> <path d="m 23,25 6,-14 h 1 v 14" style="display:inline;fill:#ffffff;fill-opacity:0.1882353;fill-rule:nonzero;stroke:none;stroke-width:0.37690103" id="path3701" /> </g> </svg> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/images/laptop.svg�����������������������������������0000664�0000000�0000000�00000000671�14215434441�0025512�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<svg fill="#000000" height="18" viewBox="0 0 24 24" width="18" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <path d="M0 0h24v24H0V0z" id="a"/> </defs> <clipPath id="b"> <use overflow="visible" xlink:href="#a"/> </clipPath> <path clip-path="url(#b)" d="M20 18c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z"/> </svg>�����������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/images/message.svg����������������������������������0000664�0000000�0000000�00000000432�14215434441�0025632�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<svg fill="#000000" height="18" viewBox="0 0 24 24" width="18" xmlns="http://www.w3.org/2000/svg"> <path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"/> <path d="M0 0h24v24H0z" fill="none"/> </svg>��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/images/open-in-browser.svg��������������������������0000664�0000000�0000000�00000000441�14215434441�0027234�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<svg fill="#000000" height="18" viewBox="0 0 24 24" width="18" xmlns="http://www.w3.org/2000/svg"> <path d="M0 0h24v24H0z" fill="none"/> <path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h4v-2H5V8h14v10h-4v2h4c1.1 0 2-.9 2-2V6c0-1.1-.89-2-2-2zm-7 6l-4 4h3v6h2v-6h3l-4-4z"/> </svg>�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/images/phone.svg������������������������������������0000664�0000000�0000000�00000000443�14215434441�0025321�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<svg fill="#000000" height="18" viewBox="0 0 24 24" width="18" xmlns="http://www.w3.org/2000/svg"> <path d="M16 1H8C6.34 1 5 2.34 5 4v16c0 1.66 1.34 3 3 3h8c1.66 0 3-1.34 3-3V4c0-1.66-1.34-3-3-3zm-2 20h-4v-1h4v1zm3.25-3H6.75V4h10.5v14z"/> <path d="M0 0h24v24H0z" fill="none"/> </svg>�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/images/tablet.svg�����������������������������������0000664�0000000�0000000�00000000721�14215434441�0025462�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<svg fill="#000000" height="18" viewBox="0 0 24 24" width="18" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <path d="M0 0h24v24H0z" id="a"/> </defs> <clipPath id="b"> <use overflow="visible" xlink:href="#a"/> </clipPath> <path clip-path="url(#b)" d="M18 0H6C4.34 0 3 1.34 3 3v18c0 1.66 1.34 3 3 3h12c1.66 0 3-1.34 3-3V3c0-1.66-1.34-3-3-3zm-4 22h-4v-1h4v1zm5.25-3H4.75V3h14.5v16z"/> </svg>�����������������������������������������������gnome-shell-extension-gsconnect-50/webextension/js/�������������������������������������������������0000775�0000000�0000000�00000000000�14215434441�0022635�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/js/background.js������������������������������������0000664�0000000�0000000�00000026256�14215434441�0025325�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; const _ABOUT = /^chrome:|^about:/; const _CONTEXTS = [ 'audio', 'page', 'frame', 'link', 'image', // FIREFOX-ONLY: mkwebext.sh will automatically remove this 'tab', 'video', ]; // Suppress errors caused by Mozilla polyfill // TODO: not sure if these are relevant anymore const _MUTE = [ 'Could not establish connection. Receiving end does not exist.', 'The message port closed before a response was received.', ]; /** * State of the extension. */ const State = { connected: false, devices: [], port: null, }; var reconnectDelay = 100; var reconnectTimer = null; var reconnectResetTimer = null; // Simple error logging function // eslint-disable-next-line no-redeclare function logError(error) { if (!_MUTE.includes(error.message)) console.error(error.message); } function toggleAction(tab = null) { try { // Disable on "about:" pages if (_ABOUT.test(tab.url)) browser.browserAction.disable(tab.id); else browser.browserAction.enable(tab.id); } catch (e) { browser.browserAction.disable(); } } /** * Send a message to the native-messaging-host * * @param {Object} message - The message to forward */ // eslint-disable-next-line no-redeclare async function postMessage(message) { try { // console.log(`WebExtension SEND: ${JSON.stringify(message)}`); if (!State.port || !message || !message.type) { console.warn('Missing message parameters'); return; } await State.port.postMessage(message); } catch (e) { logError(e); } } /** * Forward a message from the browserAction popup to the NMH * * @param {Object} message - A message from the NMH to forward * @param {*} sender - A message from the NMH to forward * @param {*} sendResponse - A message from the NMH to forward */ async function onPopupMessage(message, sender, sendResponse) { try { if (sender.url.includes('/popup.html')) await postMessage(message); } catch (e) { logError(e); } } /** * Forward a message from the NMH to the browserAction popup * * @param {Object} message - A message from the NMH to forward */ async function forwardPortMessage(message) { try { await browser.runtime.sendMessage(message); } catch (e) { logError(e); } } /** * Context Menu Item Callback * * @param {menus.OnClickData} info - Information about the item and context * @param {tabs.Tab} tab - The details of the tab where the click took place */ async function onContextItem(info, tab) { try { const [id, action] = info.menuItemId.split(':'); await postMessage({ type: 'share', data: { device: id, url: info.linkUrl || info.srcUrl || info.frameUrl || info.pageUrl, action: action, }, }); } catch (e) { logError(e); } } /** * Populate the context menu * * @param {tabs.Tab} tab - The current tab */ async function createContextMenu(tab) { try { // Clear context menu await browser.contextMenus.removeAll(); // Bail on "about:" page or no devices if (_ABOUT.test(tab.url) || State.devices.length === 0) return; // Multiple devices; we'll have at least one submenu level if (State.devices.length > 1) { await browser.contextMenus.create({ id: 'contextMenuMultipleDevices', title: browser.i18n.getMessage('contextMenuMultipleDevices'), contexts: _CONTEXTS, }); for (const device of State.devices) { if (device.share && device.telephony) { await browser.contextMenus.create({ id: device.id, title: device.name, parentId: 'contextMenuMultipleDevices', }); await browser.contextMenus.create({ id: `${device.id}:share`, title: browser.i18n.getMessage('shareMessage'), parentId: device.id, contexts: _CONTEXTS, onclick: onContextItem, }); await browser.contextMenus.create({ id: `${device.id}:telephony`, title: browser.i18n.getMessage('smsMessage'), parentId: device.id, contexts: _CONTEXTS, onclick: onContextItem, }); } else { let pluginAction, pluginName; if (device.share) { pluginAction = 'share'; pluginName = browser.i18n.getMessage('shareMessage'); } else { pluginAction = 'telephony'; pluginName = browser.i18n.getMessage('smsMessage'); } await browser.contextMenus.create({ id: `${device.id}:${pluginAction}`, title: browser.i18n.getMessage( 'contextMenuSinglePlugin', [device.name, pluginName] ), parentId: 'contextMenuMultipleDevices', contexts: _CONTEXTS, onclick: onContextItem, }); } } // One device; we'll create a top level menu } else { const device = State.devices[0]; if (device.share && device.telephony) { await browser.contextMenus.create({ id: device.id, title: device.name, contexts: _CONTEXTS, }); await browser.contextMenus.create({ id: `${device.id}:share`, title: browser.i18n.getMessage('shareMessage'), parentId: device.id, contexts: _CONTEXTS, onclick: onContextItem, }); await browser.contextMenus.create({ id: `${device.id}:telephony`, title: browser.i18n.getMessage('smsMessage'), parentId: device.id, contexts: _CONTEXTS, onclick: onContextItem, }); } else { let pluginAction, pluginName; if (device.share) { pluginAction = 'share'; pluginName = browser.i18n.getMessage('shareMessage'); } else { pluginAction = 'telephony'; pluginName = browser.i18n.getMessage('smsMessage'); } await browser.contextMenus.create({ id: `${device.id}:${pluginAction}`, title: browser.i18n.getMessage( 'contextMenuSinglePlugin', [device.name, pluginName] ), contexts: _CONTEXTS, onclick: onContextItem, }); } } } catch (e) { logError(e); } } /** * Message Handling * * @param {Object} message - A message received from the NMH */ async function onPortMessage(message) { try { // console.log(`WebExtension RECV: ${JSON.stringify(message)}`); // The native-messaging-host's connection to the service has changed if (message.type === 'connected') { State.connected = message.data; if (State.connected) postMessage({type: 'devices'}); else State.devices = []; // We're being sent a list of devices (so the NMH must be connected) } else if (message.type === 'devices') { State.connected = true; State.devices = message.data; } // Forward the message to popup.html forwardPortMessage(message); // const tabs = await browser.tabs.query({ active: true, currentWindow: true, }); createContextMenu(tabs[0]); } catch (e) { logError(e); } } /** * Callback for disconnection from the native-messaging-host * * @param {object} port - The port that is now invalid */ async function onDisconnect(port) { try { State.connected = false; State.port = null; browser.browserAction.setBadgeText({text: '\u26D4'}); browser.browserAction.setBadgeBackgroundColor({color: [198, 40, 40, 255]}); forwardPortMessage({type: 'connected', data: false}); // Clear context menu await browser.contextMenus.removeAll(); // Disconnected, cancel back-off reset if (typeof reconnectResetTimer === 'number') { window.clearTimeout(reconnectResetTimer); reconnectResetTimer = null; } // Don't queue more than one reconnect if (typeof reconnectTimer === 'number') { window.clearTimeout(reconnectTimer); reconnectTimer = null; } // Log disconnection if (browser.runtime.lastError) { const message = browser.runtime.lastError.message; console.warn(`Disconnected: ${message}`); } // Exponential back-off on reconnect reconnectTimer = window.setTimeout(connect, reconnectDelay); reconnectDelay *= 2; } catch (e) { logError(e); } } /** * Start and/or connect to the native-messaging-host */ async function connect() { try { State.port = browser.runtime.connectNative('org.gnome.shell.extensions.gsconnect'); // Clear the badge and tell the popup we're disconnected browser.browserAction.setBadgeText({text: ''}); browser.browserAction.setBadgeBackgroundColor({color: [0, 0, 0, 0]}); // Reset the back-off delay if we stay connected reconnectResetTimer = window.setTimeout(() => { reconnectDelay = 100; }, reconnectDelay * 0.9); // Start listening and request a list of available devices State.port.onDisconnect.addListener(onDisconnect); State.port.onMessage.addListener(onPortMessage); await State.port.postMessage({type: 'devices'}); } catch (e) { logError(e); } } // Forward messages from the browserAction popup browser.runtime.onMessage.addListener(onPopupMessage); // Keep browserAction up to date browser.tabs.onActivated.addListener((info) => { browser.tabs.get(info.tabId).then(toggleAction); }); browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { if (changeInfo.url) toggleAction(tab); }); // Keep contextMenu up to date browser.tabs.onActivated.addListener((info) => { browser.tabs.get(info.tabId).then(createContextMenu); }); browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { if (changeInfo.url) createContextMenu(tab); }); /** * Startup: set initial state of the browserAction and try to connect */ toggleAction(); connect(); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/js/browser-polyfill.min.js��������������������������0000664�0000000�0000000�00000023574�14215434441�0027303�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������(function(a,b){if("function"==typeof define&&define.amd)define("webextension-polyfill",["module"],b);else if("undefined"!=typeof exports)b(module);else{var c={exports:{}};b(c),a.browser=c.exports}})("undefined"==typeof globalThis?"undefined"==typeof self?this:self:globalThis,function(a){"use strict";if("undefined"==typeof browser||Object.getPrototypeOf(browser)!==Object.prototype){if("object"!=typeof chrome||!chrome||!chrome.runtime||!chrome.runtime.id)throw new Error("This script should only be loaded in a browser extension.");a.exports=(a=>{const b={alarms:{clear:{minArgs:0,maxArgs:1},clearAll:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getAll:{minArgs:0,maxArgs:0}},bookmarks:{create:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},getChildren:{minArgs:1,maxArgs:1},getRecent:{minArgs:1,maxArgs:1},getSubTree:{minArgs:1,maxArgs:1},getTree:{minArgs:0,maxArgs:0},move:{minArgs:2,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeTree:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}},browserAction:{disable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},enable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},getBadgeBackgroundColor:{minArgs:1,maxArgs:1},getBadgeText:{minArgs:1,maxArgs:1},getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},openPopup:{minArgs:0,maxArgs:0},setBadgeBackgroundColor:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setBadgeText:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},browsingData:{remove:{minArgs:2,maxArgs:2},removeCache:{minArgs:1,maxArgs:1},removeCookies:{minArgs:1,maxArgs:1},removeDownloads:{minArgs:1,maxArgs:1},removeFormData:{minArgs:1,maxArgs:1},removeHistory:{minArgs:1,maxArgs:1},removeLocalStorage:{minArgs:1,maxArgs:1},removePasswords:{minArgs:1,maxArgs:1},removePluginData:{minArgs:1,maxArgs:1},settings:{minArgs:0,maxArgs:0}},commands:{getAll:{minArgs:0,maxArgs:0}},contextMenus:{remove:{minArgs:1,maxArgs:1},removeAll:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},cookies:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:1,maxArgs:1},getAllCookieStores:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},devtools:{inspectedWindow:{eval:{minArgs:1,maxArgs:2,singleCallbackArg:!1}},panels:{create:{minArgs:3,maxArgs:3,singleCallbackArg:!0}}},downloads:{cancel:{minArgs:1,maxArgs:1},download:{minArgs:1,maxArgs:1},erase:{minArgs:1,maxArgs:1},getFileIcon:{minArgs:1,maxArgs:2},open:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},pause:{minArgs:1,maxArgs:1},removeFile:{minArgs:1,maxArgs:1},resume:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},extension:{isAllowedFileSchemeAccess:{minArgs:0,maxArgs:0},isAllowedIncognitoAccess:{minArgs:0,maxArgs:0}},history:{addUrl:{minArgs:1,maxArgs:1},deleteAll:{minArgs:0,maxArgs:0},deleteRange:{minArgs:1,maxArgs:1},deleteUrl:{minArgs:1,maxArgs:1},getVisits:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1}},i18n:{detectLanguage:{minArgs:1,maxArgs:1},getAcceptLanguages:{minArgs:0,maxArgs:0}},identity:{launchWebAuthFlow:{minArgs:1,maxArgs:1}},idle:{queryState:{minArgs:1,maxArgs:1}},management:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},getSelf:{minArgs:0,maxArgs:0},setEnabled:{minArgs:2,maxArgs:2},uninstallSelf:{minArgs:0,maxArgs:1}},notifications:{clear:{minArgs:1,maxArgs:1},create:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:0},getPermissionLevel:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},pageAction:{getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},hide:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},permissions:{contains:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},request:{minArgs:1,maxArgs:1}},runtime:{getBackgroundPage:{minArgs:0,maxArgs:0},getPlatformInfo:{minArgs:0,maxArgs:0},openOptionsPage:{minArgs:0,maxArgs:0},requestUpdateCheck:{minArgs:0,maxArgs:0},sendMessage:{minArgs:1,maxArgs:3},sendNativeMessage:{minArgs:2,maxArgs:2},setUninstallURL:{minArgs:1,maxArgs:1}},sessions:{getDevices:{minArgs:0,maxArgs:1},getRecentlyClosed:{minArgs:0,maxArgs:1},restore:{minArgs:0,maxArgs:1}},storage:{local:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},managed:{get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1}},sync:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}}},tabs:{captureVisibleTab:{minArgs:0,maxArgs:2},create:{minArgs:1,maxArgs:1},detectLanguage:{minArgs:0,maxArgs:1},discard:{minArgs:0,maxArgs:1},duplicate:{minArgs:1,maxArgs:1},executeScript:{minArgs:1,maxArgs:2},get:{minArgs:1,maxArgs:1},getCurrent:{minArgs:0,maxArgs:0},getZoom:{minArgs:0,maxArgs:1},getZoomSettings:{minArgs:0,maxArgs:1},highlight:{minArgs:1,maxArgs:1},insertCSS:{minArgs:1,maxArgs:2},move:{minArgs:2,maxArgs:2},query:{minArgs:1,maxArgs:1},reload:{minArgs:0,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeCSS:{minArgs:1,maxArgs:2},sendMessage:{minArgs:2,maxArgs:3},setZoom:{minArgs:1,maxArgs:2},setZoomSettings:{minArgs:1,maxArgs:2},update:{minArgs:1,maxArgs:2}},topSites:{get:{minArgs:0,maxArgs:0}},webNavigation:{getAllFrames:{minArgs:1,maxArgs:1},getFrame:{minArgs:1,maxArgs:1}},webRequest:{handlerBehaviorChanged:{minArgs:0,maxArgs:0}},windows:{create:{minArgs:0,maxArgs:1},get:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:1},getCurrent:{minArgs:0,maxArgs:1},getLastFocused:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}}};if(0===Object.keys(b).length)throw new Error("api-metadata.json has not been included in browser-polyfill");class c extends WeakMap{constructor(a,b=void 0){super(b),this.createItem=a}get(a){return this.has(a)||this.set(a,this.createItem(a)),super.get(a)}}const d=a=>a&&"object"==typeof a&&"function"==typeof a.then,e=(b,c)=>(...d)=>{a.runtime.lastError?b.reject(a.runtime.lastError):c.singleCallbackArg||1>=d.length&&!1!==c.singleCallbackArg?b.resolve(d[0]):b.resolve(d)},f=a=>1==a?"argument":"arguments",g=(a,b)=>function(c,...d){if(d.length<b.minArgs)throw new Error(`Expected at least ${b.minArgs} ${f(b.minArgs)} for ${a}(), got ${d.length}`);if(d.length>b.maxArgs)throw new Error(`Expected at most ${b.maxArgs} ${f(b.maxArgs)} for ${a}(), got ${d.length}`);return new Promise((f,g)=>{if(b.fallbackToNoCallback)try{c[a](...d,e({resolve:f,reject:g},b))}catch(e){console.warn(`${a} API method doesn't seem to support the callback parameter, `+"falling back to call it without a callback: ",e),c[a](...d),b.fallbackToNoCallback=!1,b.noCallback=!0,f()}else b.noCallback?(c[a](...d),f()):c[a](...d,e({resolve:f,reject:g},b))})},h=(a,b,c)=>new Proxy(b,{apply(b,d,e){return c.call(d,a,...e)}});let i=Function.call.bind(Object.prototype.hasOwnProperty);const j=(a,b={},c={})=>{let d=Object.create(null),e={has(b,c){return c in a||c in d},get(e,f,k){if(f in d)return d[f];if(!(f in a))return;let l=a[f];if("function"==typeof l){if("function"==typeof b[f])l=h(a,a[f],b[f]);else if(i(c,f)){let b=g(f,c[f]);l=h(a,a[f],b)}else l=l.bind(a);}else if("object"==typeof l&&null!==l&&(i(b,f)||i(c,f)))l=j(l,b[f],c[f]);else if(i(c,"*"))l=j(l,b[f],c["*"]);else return Object.defineProperty(d,f,{configurable:!0,enumerable:!0,get(){return a[f]},set(b){a[f]=b}}),l;return d[f]=l,l},set(b,c,e,f){return c in d?d[c]=e:a[c]=e,!0},defineProperty(a,b,c){return Reflect.defineProperty(d,b,c)},deleteProperty(a,b){return Reflect.deleteProperty(d,b)}},f=Object.create(a);return new Proxy(f,e)},k=a=>({addListener(b,c,...d){b.addListener(a.get(c),...d)},hasListener(b,c){return b.hasListener(a.get(c))},removeListener(b,c){b.removeListener(a.get(c))}});let l=!1;const m=new c(a=>"function"==typeof a?function(b,c,e){let f,g,h=!1,i=new Promise(a=>{f=function(b){l||(console.warn("Returning a Promise is the preferred way to send a reply from an onMessage/onMessageExternal listener, as the sendResponse will be removed from the specs (See https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage)",new Error().stack),l=!0),h=!0,a(b)}});try{g=a(b,c,f)}catch(a){g=Promise.reject(a)}const j=!0!==g&&d(g);if(!0!==g&&!j&&!h)return!1;const k=a=>{a.then(a=>{e(a)},a=>{let b;b=a&&(a instanceof Error||"string"==typeof a.message)?a.message:"An unexpected error occurred",e({__mozWebExtensionPolyfillReject__:!0,message:b})}).catch(a=>{console.error("Failed to send onMessage rejected reply",a)})};return j?k(g):k(i),!0}:a),n=({reject:b,resolve:c},d)=>{a.runtime.lastError?a.runtime.lastError.message==="The message port closed before a response was received."?c():b(a.runtime.lastError):d&&d.__mozWebExtensionPolyfillReject__?b(new Error(d.message)):c(d)},o=(a,b,c,...d)=>{if(d.length<b.minArgs)throw new Error(`Expected at least ${b.minArgs} ${f(b.minArgs)} for ${a}(), got ${d.length}`);if(d.length>b.maxArgs)throw new Error(`Expected at most ${b.maxArgs} ${f(b.maxArgs)} for ${a}(), got ${d.length}`);return new Promise((a,b)=>{const e=n.bind(null,{resolve:a,reject:b});d.push(e),c.sendMessage(...d)})},p={runtime:{onMessage:k(m),onMessageExternal:k(m),sendMessage:o.bind(null,"sendMessage",{minArgs:1,maxArgs:3})},tabs:{sendMessage:o.bind(null,"sendMessage",{minArgs:2,maxArgs:3})}},q={clear:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}};return b.privacy={network:{"*":q},services:{"*":q},websites:{"*":q}},j(a,p,b)})(chrome)}else a.exports=browser}); //# sourceMappingURL=browser-polyfill.min.js.map // webextension-polyfill v.0.6.0 (https://github.com/mozilla/webextension-polyfill) /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ ������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/js/browser-polyfill.min.js.map����������������������0000664�0000000�0000000�00000151430�14215434441�0030050�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{"version":3,"sources":["browser-polyfill.js"],"names":["Object","extensionAPIs","apiMetadata","constructor","items","get","isThenable","value","makeCallback","promise","metadata","callbackArgs","pluralizeArguments","numArgs","wrapAsyncFunction","args","minArgs","name","length","maxArgs","target","resolve","reject","console","wrapMethod","apply","wrapper","hasOwnProperty","Function","wrapObject","wrappers","cache","handlers","has","prop","configurable","enumerable","set","defineProperty","Reflect","deleteProperty","proxyTarget","wrapEvent","wrapperMap","addListener","hasListener","removeListener","loggedSendResponseDeprecationWarning","onMessageWrappers","listener","didCallSendResponse","sendResponsePromise","wrappedSendResponse","result","Promise","isResultThenable","sendPromisedResult","msg","sendResponse","error","message","__mozWebExtensionPolyfillReject__","err","wrappedSendMessageCallback","reply","wrappedSendMessage","wrappedCb","apiNamespaceObj","staticWrappers","runtime","onMessage","onMessageExternal","sendMessage","tabs","settingMetadata","clear","network","services","websites","chrome","module","wrapAPIs"],"mappings":"gSAMA,aAEA,GAAI,WAAA,QAAA,CAAA,OAAA,EAAkCA,MAAM,CAANA,cAAAA,CAAAA,OAAAA,IAAmCA,MAAM,CAA/E,SAAA,CAA2F,CAkoCzF,GAAI,QAAA,QAAA,CAAA,MAAA,EAA6B,CAA7B,MAAA,EAAwC,CAAC+E,MAAM,CAA/C,OAAA,EAA2D,CAACA,MAAM,CAANA,OAAAA,CAAhE,EAAA,CACE,KAAM,IAAA,CAAA,KAAA,CAAN,2DAAM,CAAN,CAKFC,CAAM,CAANA,OAAAA,CAAiBC,CA/nCAhF,CAAa,EAAI,CAIhC,KAAMC,CAAAA,CAAW,CAAG,CAClB,OAAU,CACR,MAAS,CACP,QADO,CAAA,CAEP,QAAW,CAFJ,CADD,CAKR,SAAY,CACV,QADU,CAAA,CAEV,QAAW,CAFD,CALJ,CASR,IAAO,CACL,QADK,CAAA,CAEL,QAAW,CAFN,CATC,CAaR,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CAbF,CADQ,CAmBlB,UAAa,CACX,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CADC,CAKX,IAAO,CACL,QADK,CAAA,CAEL,QAAW,CAFN,CALI,CASX,YAAe,CACb,QADa,CAAA,CAEb,QAAW,CAFE,CATJ,CAaX,UAAa,CACX,QADW,CAAA,CAEX,QAAW,CAFA,CAbF,CAiBX,WAAc,CACZ,QADY,CAAA,CAEZ,QAAW,CAFC,CAjBH,CAqBX,QAAW,CACT,QADS,CAAA,CAET,QAAW,CAFF,CArBA,CAyBX,KAAQ,CACN,QADM,CAAA,CAEN,QAAW,CAFL,CAzBG,CA6BX,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CA7BC,CAiCX,WAAc,CACZ,QADY,CAAA,CAEZ,QAAW,CAFC,CAjCH,CAqCX,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CArCC,CAyCX,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CAzCC,CAnBK,CAiElB,cAAiB,CACf,QAAW,CACT,QADS,CAAA,CAET,QAFS,CAAA,CAGT,uBAHS,CADI,CAMf,OAAU,CACR,QADQ,CAAA,CAER,QAFQ,CAAA,CAGR,uBAHQ,CANK,CAWf,wBAA2B,CACzB,QADyB,CAAA,CAEzB,QAAW,CAFc,CAXZ,CAef,aAAgB,CACd,QADc,CAAA,CAEd,QAAW,CAFG,CAfD,CAmBf,SAAY,CACV,QADU,CAAA,CAEV,QAAW,CAFD,CAnBG,CAuBf,SAAY,CACV,QADU,CAAA,CAEV,QAAW,CAFD,CAvBG,CA2Bf,UAAa,CACX,QADW,CAAA,CAEX,QAAW,CAFA,CA3BE,CA+Bf,wBAA2B,CACzB,QADyB,CAAA,CAEzB,QAFyB,CAAA,CAGzB,uBAHyB,CA/BZ,CAoCf,aAAgB,CACd,QADc,CAAA,CAEd,QAFc,CAAA,CAGd,uBAHc,CApCD,CAyCf,QAAW,CACT,QADS,CAAA,CAET,QAAW,CAFF,CAzCI,CA6Cf,SAAY,CACV,QADU,CAAA,CAEV,QAFU,CAAA,CAGV,uBAHU,CA7CG,CAkDf,SAAY,CACV,QADU,CAAA,CAEV,QAFU,CAAA,CAGV,uBAHU,CAlDG,CAjEC,CAyHlB,aAAgB,CACd,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CADI,CAKd,YAAe,CACb,QADa,CAAA,CAEb,QAAW,CAFE,CALD,CASd,cAAiB,CACf,QADe,CAAA,CAEf,QAAW,CAFI,CATH,CAad,gBAAmB,CACjB,QADiB,CAAA,CAEjB,QAAW,CAFM,CAbL,CAiBd,eAAkB,CAChB,QADgB,CAAA,CAEhB,QAAW,CAFK,CAjBJ,CAqBd,cAAiB,CACf,QADe,CAAA,CAEf,QAAW,CAFI,CArBH,CAyBd,mBAAsB,CACpB,QADoB,CAAA,CAEpB,QAAW,CAFS,CAzBR,CA6Bd,gBAAmB,CACjB,QADiB,CAAA,CAEjB,QAAW,CAFM,CA7BL,CAiCd,iBAAoB,CAClB,QADkB,CAAA,CAElB,QAAW,CAFO,CAjCN,CAqCd,SAAY,CACV,QADU,CAAA,CAEV,QAAW,CAFD,CArCE,CAzHE,CAmKlB,SAAY,CACV,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CADA,CAnKM,CAyKlB,aAAgB,CACd,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CADI,CAKd,UAAa,CACX,QADW,CAAA,CAEX,QAAW,CAFA,CALC,CASd,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CATI,CAzKE,CAuLlB,QAAW,CACT,IAAO,CACL,QADK,CAAA,CAEL,QAAW,CAFN,CADE,CAKT,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CALD,CAST,mBAAsB,CACpB,QADoB,CAAA,CAEpB,QAAW,CAFS,CATb,CAaT,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CAbD,CAiBT,IAAO,CACL,QADK,CAAA,CAEL,QAAW,CAFN,CAjBE,CAvLO,CA6MlB,SAAY,CACV,gBAAmB,CACjB,KAAQ,CACN,QADM,CAAA,CAEN,QAFM,CAAA,CAGN,oBAHM,CADS,CADT,CAQV,OAAU,CACR,OAAU,CACR,QADQ,CAAA,CAER,QAFQ,CAAA,CAGR,oBAHQ,CADF,CARA,CA7MM,CA6NlB,UAAa,CACX,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CADC,CAKX,SAAY,CACV,QADU,CAAA,CAEV,QAAW,CAFD,CALD,CASX,MAAS,CACP,QADO,CAAA,CAEP,QAAW,CAFJ,CATE,CAaX,YAAe,CACb,QADa,CAAA,CAEb,QAAW,CAFE,CAbJ,CAiBX,KAAQ,CACN,QADM,CAAA,CAEN,QAFM,CAAA,CAGN,uBAHM,CAjBG,CAsBX,MAAS,CACP,QADO,CAAA,CAEP,QAAW,CAFJ,CAtBE,CA0BX,WAAc,CACZ,QADY,CAAA,CAEZ,QAAW,CAFC,CA1BH,CA8BX,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CA9BC,CAkCX,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CAlCC,CAsCX,KAAQ,CACN,QADM,CAAA,CAEN,QAFM,CAAA,CAGN,uBAHM,CAtCG,CA7NK,CAyQlB,UAAa,CACX,0BAA6B,CAC3B,QAD2B,CAAA,CAE3B,QAAW,CAFgB,CADlB,CAKX,yBAA4B,CAC1B,QAD0B,CAAA,CAE1B,QAAW,CAFe,CALjB,CAzQK,CAmRlB,QAAW,CACT,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CADD,CAKT,UAAa,CACX,QADW,CAAA,CAEX,QAAW,CAFA,CALJ,CAST,YAAe,CACb,QADa,CAAA,CAEb,QAAW,CAFE,CATN,CAaT,UAAa,CACX,QADW,CAAA,CAEX,QAAW,CAFA,CAbJ,CAiBT,UAAa,CACX,QADW,CAAA,CAEX,QAAW,CAFA,CAjBJ,CAqBT,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CArBD,CAnRO,CA6SlB,KAAQ,CACN,eAAkB,CAChB,QADgB,CAAA,CAEhB,QAAW,CAFK,CADZ,CAKN,mBAAsB,CACpB,QADoB,CAAA,CAEpB,QAAW,CAFS,CALhB,CA7SU,CAuTlB,SAAY,CACV,kBAAqB,CACnB,QADmB,CAAA,CAEnB,QAAW,CAFQ,CADX,CAvTM,CA6TlB,KAAQ,CACN,WAAc,CACZ,QADY,CAAA,CAEZ,QAAW,CAFC,CADR,CA7TU,CAmUlB,WAAc,CACZ,IAAO,CACL,QADK,CAAA,CAEL,QAAW,CAFN,CADK,CAKZ,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CALE,CASZ,QAAW,CACT,QADS,CAAA,CAET,QAAW,CAFF,CATC,CAaZ,WAAc,CACZ,QADY,CAAA,CAEZ,QAAW,CAFC,CAbF,CAiBZ,cAAiB,CACf,QADe,CAAA,CAEf,QAAW,CAFI,CAjBL,CAnUI,CAyVlB,cAAiB,CACf,MAAS,CACP,QADO,CAAA,CAEP,QAAW,CAFJ,CADM,CAKf,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CALK,CASf,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CATK,CAaf,mBAAsB,CACpB,QADoB,CAAA,CAEpB,QAAW,CAFS,CAbP,CAiBf,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CAjBK,CAzVC,CA+WlB,WAAc,CACZ,SAAY,CACV,QADU,CAAA,CAEV,QAAW,CAFD,CADA,CAKZ,SAAY,CACV,QADU,CAAA,CAEV,QAAW,CAFD,CALA,CASZ,KAAQ,CACN,QADM,CAAA,CAEN,QAFM,CAAA,CAGN,uBAHM,CATI,CAcZ,QAAW,CACT,QADS,CAAA,CAET,QAAW,CAFF,CAdC,CAkBZ,SAAY,CACV,QADU,CAAA,CAEV,QAFU,CAAA,CAGV,uBAHU,CAlBA,CAuBZ,SAAY,CACV,QADU,CAAA,CAEV,QAFU,CAAA,CAGV,uBAHU,CAvBA,CA4BZ,KAAQ,CACN,QADM,CAAA,CAEN,QAFM,CAAA,CAGN,uBAHM,CA5BI,CA/WI,CAiZlB,YAAe,CACb,SAAY,CACV,QADU,CAAA,CAEV,QAAW,CAFD,CADC,CAKb,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CALG,CASb,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CATG,CAab,QAAW,CACT,QADS,CAAA,CAET,QAAW,CAFF,CAbE,CAjZG,CAmalB,QAAW,CACT,kBAAqB,CACnB,QADmB,CAAA,CAEnB,QAAW,CAFQ,CADZ,CAKT,gBAAmB,CACjB,QADiB,CAAA,CAEjB,QAAW,CAFM,CALV,CAST,gBAAmB,CACjB,QADiB,CAAA,CAEjB,QAAW,CAFM,CATV,CAaT,mBAAsB,CACpB,QADoB,CAAA,CAEpB,QAAW,CAFS,CAbb,CAiBT,YAAe,CACb,QADa,CAAA,CAEb,QAAW,CAFE,CAjBN,CAqBT,kBAAqB,CACnB,QADmB,CAAA,CAEnB,QAAW,CAFQ,CArBZ,CAyBT,gBAAmB,CACjB,QADiB,CAAA,CAEjB,QAAW,CAFM,CAzBV,CAnaO,CAiclB,SAAY,CACV,WAAc,CACZ,QADY,CAAA,CAEZ,QAAW,CAFC,CADJ,CAKV,kBAAqB,CACnB,QADmB,CAAA,CAEnB,QAAW,CAFQ,CALX,CASV,QAAW,CACT,QADS,CAAA,CAET,QAAW,CAFF,CATD,CAjcM,CA+clB,QAAW,CACT,MAAS,CACP,MAAS,CACP,QADO,CAAA,CAEP,QAAW,CAFJ,CADF,CAKP,IAAO,CACL,QADK,CAAA,CAEL,QAAW,CAFN,CALA,CASP,cAAiB,CACf,QADe,CAAA,CAEf,QAAW,CAFI,CATV,CAaP,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CAbH,CAiBP,IAAO,CACL,QADK,CAAA,CAEL,QAAW,CAFN,CAjBA,CADA,CAuBT,QAAW,CACT,IAAO,CACL,QADK,CAAA,CAEL,QAAW,CAFN,CADE,CAKT,cAAiB,CACf,QADe,CAAA,CAEf,QAAW,CAFI,CALR,CAvBF,CAiCT,KAAQ,CACN,MAAS,CACP,QADO,CAAA,CAEP,QAAW,CAFJ,CADH,CAKN,IAAO,CACL,QADK,CAAA,CAEL,QAAW,CAFN,CALD,CASN,cAAiB,CACf,QADe,CAAA,CAEf,QAAW,CAFI,CATX,CAaN,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CAbJ,CAiBN,IAAO,CACL,QADK,CAAA,CAEL,QAAW,CAFN,CAjBD,CAjCC,CA/cO,CAugBlB,KAAQ,CACN,kBAAqB,CACnB,QADmB,CAAA,CAEnB,QAAW,CAFQ,CADf,CAKN,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CALJ,CASN,eAAkB,CAChB,QADgB,CAAA,CAEhB,QAAW,CAFK,CATZ,CAaN,QAAW,CACT,QADS,CAAA,CAET,QAAW,CAFF,CAbL,CAiBN,UAAa,CACX,QADW,CAAA,CAEX,QAAW,CAFA,CAjBP,CAqBN,cAAiB,CACf,QADe,CAAA,CAEf,QAAW,CAFI,CArBX,CAyBN,IAAO,CACL,QADK,CAAA,CAEL,QAAW,CAFN,CAzBD,CA6BN,WAAc,CACZ,QADY,CAAA,CAEZ,QAAW,CAFC,CA7BR,CAiCN,QAAW,CACT,QADS,CAAA,CAET,QAAW,CAFF,CAjCL,CAqCN,gBAAmB,CACjB,QADiB,CAAA,CAEjB,QAAW,CAFM,CArCb,CAyCN,UAAa,CACX,QADW,CAAA,CAEX,QAAW,CAFA,CAzCP,CA6CN,UAAa,CACX,QADW,CAAA,CAEX,QAAW,CAFA,CA7CP,CAiDN,KAAQ,CACN,QADM,CAAA,CAEN,QAAW,CAFL,CAjDF,CAqDN,MAAS,CACP,QADO,CAAA,CAEP,QAAW,CAFJ,CArDH,CAyDN,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CAzDJ,CA6DN,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CA7DJ,CAiEN,UAAa,CACX,QADW,CAAA,CAEX,QAAW,CAFA,CAjEP,CAqEN,YAAe,CACb,QADa,CAAA,CAEb,QAAW,CAFE,CArET,CAyEN,QAAW,CACT,QADS,CAAA,CAET,QAAW,CAFF,CAzEL,CA6EN,gBAAmB,CACjB,QADiB,CAAA,CAEjB,QAAW,CAFM,CA7Eb,CAiFN,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CAjFJ,CAvgBU,CA6lBlB,SAAY,CACV,IAAO,CACL,QADK,CAAA,CAEL,QAAW,CAFN,CADG,CA7lBM,CAmmBlB,cAAiB,CACf,aAAgB,CACd,QADc,CAAA,CAEd,QAAW,CAFG,CADD,CAKf,SAAY,CACV,QADU,CAAA,CAEV,QAAW,CAFD,CALG,CAnmBC,CA6mBlB,WAAc,CACZ,uBAA0B,CACxB,QADwB,CAAA,CAExB,QAAW,CAFa,CADd,CA7mBI,CAmnBlB,QAAW,CACT,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CADD,CAKT,IAAO,CACL,QADK,CAAA,CAEL,QAAW,CAFN,CALE,CAST,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CATD,CAaT,WAAc,CACZ,QADY,CAAA,CAEZ,QAAW,CAFC,CAbL,CAiBT,eAAkB,CAChB,QADgB,CAAA,CAEhB,QAAW,CAFK,CAjBT,CAqBT,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CArBD,CAyBT,OAAU,CACR,QADQ,CAAA,CAER,QAAW,CAFH,CAzBD,CAnnBO,CAApB,CAmpBA,GAAA,CAAIF,GAAAA,MAAM,CAANA,IAAAA,CAAAA,CAAAA,EAAAA,MAAJ,CACE,KAAM,IAAA,CAAA,KAAA,CAAN,6DAAM,CAAN,CAaF,KAAA,CAAA,CAAA,QAAA,CAAA,OAAqC,CACnCG,WAAW,CAAA,CAAA,CAAaC,CAAb,OAAA,CAAgC,CACzC,MAAA,CAAA,CADyC,CAEzC,KAAA,UAAA,CAAA,CACD,CAEDC,GAAG,CAAA,CAAA,CAAM,CAKP,MAJK,MAAA,GAAA,CAAL,CAAK,CAIL,EAHE,KAAA,GAAA,CAAA,CAAA,CAAc,KAAA,UAAA,CAAd,CAAc,CAAd,CAGF,CAAO,MAAA,GAAA,CAAP,CAAO,CACR,CAZkC,CArqBL,KA2rB1BC,CAAAA,CAAU,CAAGC,CAAK,EACfA,CAAK,EAALA,QAAS,QAAA,CAAA,CAATA,EAAP,UAA6C,QAAOA,CAAAA,CAAK,CAAZ,IA5rBf,CA0tB1BC,CAAY,CAAG,CAAA,CAAA,CAAA,CAAA,GACZ,CAAC,GAAD,CAAA,GAAqB,CACtBP,CAAa,CAAbA,OAAAA,CAAJ,SAD0B,CAExBQ,CAAO,CAAPA,MAAAA,CAAeR,CAAa,CAAbA,OAAAA,CAAfQ,SAAAA,CAFwB,CAGfC,CAAQ,CAARA,iBAAAA,EACCC,CAAAA,EAAAA,CAAY,CAAZA,MAAAA,EAA4BD,KAAAA,CAAQ,CADzC,iBAHmB,CAKxBD,CAAO,CAAPA,OAAAA,CAAgBE,CAAY,CAA5BF,CAA4B,CAA5BA,CALwB,CAOxBA,CAAO,CAAPA,OAAAA,CAAAA,CAAAA,CAPJ,CA3tB8B,CAuuB1BG,CAAkB,CAAIC,CAAD,EAAaA,CAAAA,EAAAA,CAAO,CAAPA,UAAO,CAA/C,WAvuBgC,CA+vB1BC,CAAiB,CAAG,CAAA,CAAA,CAAA,CAAA,GACjB,SAAA,CAAA,CAAsC,GAAtC,CAAA,CAA+C,CACpD,GAAIC,CAAI,CAAJA,MAAAA,CAAcL,CAAQ,CAA1B,OAAA,CACE,KAAM,IAAA,CAAA,KAAA,CAAW,qBAAoBA,CAAQ,CAACM,OAAQ,IAAGJ,CAAkB,CAACF,CAAQ,CAAT,OAAA,CAAmB,QAAOO,CAAK,WAAUF,CAAI,CAACG,MAAzH,EAAM,CAAN,CAGF,GAAIH,CAAI,CAAJA,MAAAA,CAAcL,CAAQ,CAA1B,OAAA,CACE,KAAM,IAAA,CAAA,KAAA,CAAW,oBAAmBA,CAAQ,CAACS,OAAQ,IAAGP,CAAkB,CAACF,CAAQ,CAAT,OAAA,CAAmB,QAAOO,CAAK,WAAUF,CAAI,CAACG,MAAxH,EAAM,CAAN,CAGF,MAAO,IAAA,CAAA,OAAA,CAAY,CAAA,CAAA,CAAA,CAAA,GAAqB,CACtC,GAAIR,CAAQ,CAAZ,oBAAA,CAIE,GAAI,CACFU,CAAM,CAANA,CAAM,CAANA,CAAa,GAAbA,CAAAA,CAAsBZ,CAAY,CAAC,CAACa,OAAD,CAACA,CAAD,CAAUC,MAAAA,CAAAA,CAAV,CAAD,CAAlCF,CAAkC,CAAlCA,CADF,CAEE,MAAA,CAAA,CAAgB,CAChBG,OAAO,CAAPA,IAAAA,CAAc,GAAEN,CAAH,8DAAC,CAAdM,8CAAAA,CAAAA,CAAAA,CADgB,CAIhBH,CAAM,CAANA,CAAM,CAANA,CAAa,GAJG,CAIhBA,CAJgB,CAQhBV,CAAQ,CAARA,oBAAAA,GARgB,CAShBA,CAAQ,CAARA,UAAAA,GATgB,CAWhBW,CAAO,EACR,CAlBH,IAmBWX,CAAAA,CAAQ,CAAZ,UAnBP,EAoBEU,CAAM,CAANA,CAAM,CAANA,CAAa,GAAbA,CAAAA,CApBF,CAqBEC,CAAO,EArBT,EAuBED,CAAM,CAANA,CAAM,CAANA,CAAa,GAAbA,CAAAA,CAAsBZ,CAAY,CAAC,CAACa,OAAD,CAACA,CAAD,CAAUC,MAAAA,CAAAA,CAAV,CAAD,CAAlCF,CAAkC,CAAlCA,CAxBJ,CAAO,CATT,CAhwB8B,CA0zB1BI,CAAU,CAAG,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,GACV,GAAA,CAAA,KAAA,CAAA,CAAA,CAAkB,CACvBC,KAAK,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAA8B,CACjC,MAAOC,CAAAA,CAAO,CAAPA,IAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAA8B,GAArC,CAAOA,CACR,CAHsB,CAAlB,CA3zBuB,CAk0BhC,GAAIC,CAAAA,CAAc,CAAGC,QAAQ,CAARA,IAAAA,CAAAA,IAAAA,CAAmB5B,MAAM,CAANA,SAAAA,CAAxC,cAAqB4B,CAArB,CAl0BgC,KA21B1BC,CAAAA,CAAU,CAAG,CAAA,CAAA,CAASC,CAAQ,CAAjB,EAAA,CAAwBpB,CAAQ,CAAhC,EAAA,GAA0C,IACvDqB,CAAAA,CAAK,CAAG/B,MAAM,CAANA,MAAAA,CAAZ,IAAYA,CAD+C,CAEvDgC,CAAQ,CAAG,CACbC,GAAG,CAAA,CAAA,CAAA,CAAA,CAAoB,CACrB,MAAOC,CAAAA,CAAI,GAAJA,CAAAA,CAAAA,EAAkBA,CAAI,GAA7B,CAAA,CAFW,CAAA,CAKb7B,GAAG,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAA8B,CAC/B,GAAI6B,CAAI,GAAR,CAAA,CAAA,CACE,MAAOH,CAAAA,CAAK,CAAZ,CAAY,CAAZ,CAGF,GAAI,EAAEG,CAAI,GAAV,CAAA,CAAI,CAAJ,CACE,OAGF,GAAI3B,CAAAA,CAAK,CAAGa,CAAM,CAAlB,CAAkB,CAAlB,CAEA,GAAA,UAAI,QAAA,CAAA,CAAJ,EAIE,GAAA,UAAI,QAAOU,CAAAA,CAAQ,CAAf,CAAe,CAAnB,CAEEvB,CAAK,CAAGiB,CAAU,CAAA,CAAA,CAASJ,CAAM,CAAf,CAAe,CAAf,CAAuBU,CAAQ,CAAjDvB,CAAiD,CAA/B,CAFpB,KAGO,IAAIoB,CAAc,CAAA,CAAA,CAAlB,CAAkB,CAAlB,CAAoC,CAGzC,GAAID,CAAAA,CAAO,CAAGZ,CAAiB,CAAA,CAAA,CAAOJ,CAAQ,CAA9C,CAA8C,CAAf,CAA/B,CACAH,CAAK,CAAGiB,CAAU,CAAA,CAAA,CAASJ,CAAM,CAAf,CAAe,CAAf,CAAlBb,CAAkB,CAJb,CAAA,IAQLA,CAAAA,CAAK,CAAGA,CAAK,CAALA,IAAAA,CAARA,CAAQA,CARH,CAPT,KAiBO,IAAI,QAAA,QAAA,CAAA,CAAA,EAAA,IAA6BA,GAAAA,CAA7B,GACCoB,CAAc,CAAA,CAAA,CAAdA,CAAc,CAAdA,EACAA,CAAc,CAAA,CAAA,CAFnB,CAEmB,CAFf,CAAJ,CAMLpB,CAAK,CAAGsB,CAAU,CAAA,CAAA,CAAQC,CAAQ,CAAhB,CAAgB,CAAhB,CAAwBpB,CAAQ,CAAlDH,CAAkD,CAAhC,CANb,KAOA,IAAIoB,CAAc,CAAA,CAAA,CAAlB,GAAkB,CAAlB,CAELpB,CAAK,CAAGsB,CAAU,CAAA,CAAA,CAAQC,CAAQ,CAAhB,CAAgB,CAAhB,CAAwBpB,CAAQ,CAAlDH,GAAkD,CAAhC,CAFb,KAiBL,OAXAP,CAAAA,MAAM,CAANA,cAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAmC,CACjCmC,YADiC,GAAA,CAEjCC,UAFiC,GAAA,CAGjC/B,GAAG,EAAG,CACJ,MAAOe,CAAAA,CAAM,CAAb,CAAa,CAJkB,CAAA,CAMjCiB,GAAG,CAAA,CAAA,CAAQ,CACTjB,CAAM,CAANA,CAAM,CAANA,CAAAA,CACD,CARgC,CAAnCpB,CAWA,CAAA,CAAA,CAIF,MADA+B,CAAAA,CAAK,CAALA,CAAK,CAALA,CAAAA,CACA,CAAA,CA7DW,CAAA,CAgEbM,GAAG,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAqC,CAMtC,MALIH,CAAAA,CAAI,GAAR,CAAA,CAKA,CAJEH,CAAK,CAALA,CAAK,CAALA,CAAAA,CAIF,CAFEX,CAAM,CAANA,CAAM,CAANA,CAAAA,CAEF,GAtEW,CAAA,CAyEbkB,cAAc,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAA0B,CACtC,MAAOC,CAAAA,OAAO,CAAPA,cAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAP,CAAOA,CA1EI,CAAA,CA6EbC,cAAc,CAAA,CAAA,CAAA,CAAA,CAAoB,CAChC,MAAOD,CAAAA,OAAO,CAAPA,cAAAA,CAAAA,CAAAA,CAAP,CAAOA,CACR,CA/EY,CAF4C,CA8FvDE,CAAW,CAAGzC,MAAM,CAANA,MAAAA,CAAlB,CAAkBA,CA9FyC,CA+F3D,MAAO,IAAA,CAAA,KAAA,CAAA,CAAA,CAAP,CAAO,CA/FT,CA31BgC,CA68B1B0C,CAAS,CAAGC,CAAU,GAAK,CAC/BC,WAAW,CAAA,CAAA,CAAA,CAAA,CAAmB,GAAnB,CAAA,CAA4B,CACrCxB,CAAM,CAANA,WAAAA,CAAmBuB,CAAU,CAAVA,GAAAA,CAAnBvB,CAAmBuB,CAAnBvB,CAA6C,GAA7CA,CAAAA,CAF6B,CAAA,CAK/ByB,WAAW,CAAA,CAAA,CAAA,CAAA,CAAmB,CAC5B,MAAOzB,CAAAA,CAAM,CAANA,WAAAA,CAAmBuB,CAAU,CAAVA,GAAAA,CAA1B,CAA0BA,CAAnBvB,CANsB,CAAA,CAS/B0B,cAAc,CAAA,CAAA,CAAA,CAAA,CAAmB,CAC/B1B,CAAM,CAANA,cAAAA,CAAsBuB,CAAU,CAAVA,GAAAA,CAAtBvB,CAAsBuB,CAAtBvB,CACD,CAX8B,CAAL,CA78BI,CA49BhC,GAAI2B,CAAAA,CAAJ,GAAA,CA59BgC,KA89B1BC,CAAAA,CAAiB,CAAG,GAAA,CAAA,CAAA,CAAmBC,CAAQ,EACnD,UAAI,QAAA,CAAA,CAD+C,CAsB5C,SAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAkD,IAGvD,CAAA,CAHuD,CAevD,CAfuD,CACnDC,CAAJ,GADuD,CAInDC,CAAmB,CAAG,GAAA,CAAA,OAAA,CAAY9B,CAAO,EAAI,CAC/C+B,CAAmB,CAAG,SAAA,CAAA,CAAmB,CACvC,CADuC,GAErC7B,OAAO,CAAPA,IAAAA,CApgC6E,wPAogC7EA,CAAgD,GAAA,CAAA,KAAA,GAAhDA,KAAAA,CAFqC,CAGrCwB,CAAAA,GAHqC,EAKvCG,CAAAA,GALuC,CAMvC7B,CAAO,CAAPA,CAAO,CANT+B,CADF,CAA0B,CAJ6B,CAgBvD,GAAI,CACFC,CAAM,CAAGJ,CAAQ,CAAA,CAAA,CAAA,CAAA,CAAjBI,CAAiB,CADnB,CAEE,MAAA,CAAA,CAAY,CACZA,CAAM,CAAGC,OAAO,CAAPA,MAAAA,CAATD,CAASC,CACV,CAED,KAAMC,CAAAA,CAAgB,CAAGF,KAAAA,CAAAA,EAAmB/C,CAAU,CAtBC,CAsBD,CAAtD,CAKA,GAAI+C,KAAAA,CAAAA,EAAmB,CAAnBA,CAAAA,EAAwC,CAA5C,CAAA,CACE,SAOF,KAAMG,CAAAA,CAAkB,CAAI/C,CAAD,EAAa,CACtCA,CAAO,CAAPA,IAAAA,CAAagD,CAAG,EAAI,CAElBC,CAAY,CAAZA,CAAY,CAFdjD,CAAAA,CAGGkD,CAAK,EAAI,CAGV,GAAA,CAAA,CAAA,CAGEC,CANQ,CAIND,CAAK,GAAKA,CAAK,WAALA,CAAAA,KAAAA,EAAd,QACI,QAAOA,CAAAA,CAAK,CAAZ,OADK,CAJC,CAMEA,CAAK,CAAfC,OANQ,CAQRA,8BARQ,CAWVF,CAAY,CAAC,CACXG,iCADW,GAAA,CAEXD,OAAAA,CAAAA,CAFW,CAAD,CAddnD,CAAAA,EAAAA,KAAAA,CAkBSqD,CAAG,EAAI,CAEdvC,OAAO,CAAPA,KAAAA,CAAAA,yCAAAA,CAAAA,CAAAA,CApBFd,CAAAA,CApCqD,CAmCvD,CAmCA,MAPA,CAAA,CAOA,CANE+C,CAAkB,CAAlBA,CAAkB,CAMpB,CAJEA,CAAkB,CAAlBA,CAAkB,CAIpB,GAtEF,CAtBmD,CAEjD,CAFsB,CA99BM,CA8jC1BO,CAA0B,CAAG,CAAC,CAACzC,MAAD,CAACA,CAAD,CAASD,OAAAA,CAAAA,CAAT,CAAD,CAAA,CAAA,GAA8B,CAC3DpB,CAAa,CAAbA,OAAAA,CAAJ,SAD+D,CAKzDA,CAAa,CAAbA,OAAAA,CAAAA,SAAAA,CAAJ,OAAIA,GA3kCV,yDAskCmE,CAM3DoB,CAAO,EANoD,CAQ3DC,CAAM,CAACrB,CAAa,CAAbA,OAAAA,CAAPqB,SAAM,CARqD,CAUpD0C,CAAK,EAAIA,CAAK,CAAlB,iCAVwD,CAa7D1C,CAAM,CAAC,GAAA,CAAA,KAAA,CAAU0C,CAAK,CAAtB1C,OAAO,CAAD,CAbuD,CAe7DD,CAAO,CAAPA,CAAO,CAfX,CA9jCgC,CAilC1B4C,CAAkB,CAAG,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAkC,GAAlC,CAAA,GAA8C,CACvE,GAAIlD,CAAI,CAAJA,MAAAA,CAAcL,CAAQ,CAA1B,OAAA,CACE,KAAM,IAAA,CAAA,KAAA,CAAW,qBAAoBA,CAAQ,CAACM,OAAQ,IAAGJ,CAAkB,CAACF,CAAQ,CAAT,OAAA,CAAmB,QAAOO,CAAK,WAAUF,CAAI,CAACG,MAAzH,EAAM,CAAN,CAGF,GAAIH,CAAI,CAAJA,MAAAA,CAAcL,CAAQ,CAA1B,OAAA,CACE,KAAM,IAAA,CAAA,KAAA,CAAW,oBAAmBA,CAAQ,CAACS,OAAQ,IAAGP,CAAkB,CAACF,CAAQ,CAAT,OAAA,CAAmB,QAAOO,CAAK,WAAUF,CAAI,CAACG,MAAxH,EAAM,CAAN,CAGF,MAAO,IAAA,CAAA,OAAA,CAAY,CAAA,CAAA,CAAA,CAAA,GAAqB,CACtC,KAAMgD,CAAAA,CAAS,CAAG,CAA0B,CAA1B,IAAA,CAAA,IAAA,CAAsC,CAAC7C,OAAD,CAACA,CAAD,CAAUC,MAAAA,CAAAA,CAAV,CAAtC,CAAlB,CACAP,CAAI,CAAJA,IAAAA,CAAAA,CAAAA,CAFsC,CAGtCoD,CAAe,CAAfA,WAAAA,CAA4B,GAA5BA,CAAAA,CAHF,CAAO,CATT,CAjlCgC,CAimC1BC,CAAc,CAAG,CACrBC,OAAO,CAAE,CACPC,SAAS,CAAE5B,CAAS,CADb,CACa,CADb,CAEP6B,iBAAiB,CAAE7B,CAAS,CAFrB,CAEqB,CAFrB,CAGP8B,WAAW,CAAE,CAAkB,CAAlB,IAAA,CAAA,IAAA,CAAA,aAAA,CAA6C,CAACxD,OAAO,CAAR,CAAA,CAAaG,OAAO,CAAE,CAAtB,CAA7C,CAHN,CADY,CAMrBsD,IAAI,CAAE,CACJD,WAAW,CAAE,CAAkB,CAAlB,IAAA,CAAA,IAAA,CAAA,aAAA,CAA6C,CAACxD,OAAO,CAAR,CAAA,CAAaG,OAAO,CAAE,CAAtB,CAA7C,CADT,CANe,CAjmCS,CA2mC1BuD,CAAe,CAAG,CACtBC,KAAK,CAAE,CAAC3D,OAAO,CAAR,CAAA,CAAaG,OAAO,CAAE,CAAtB,CADe,CAEtBd,GAAG,CAAE,CAACW,OAAO,CAAR,CAAA,CAAaG,OAAO,CAAE,CAAtB,CAFiB,CAGtBkB,GAAG,CAAE,CAACrB,OAAO,CAAR,CAAA,CAAaG,OAAO,CAAE,CAAtB,CAHiB,CA3mCQ,CAsnChC,MANAjB,CAAAA,CAAW,CAAXA,OAAAA,CAAsB,CACpB0E,OAAO,CAAE,CAAC,IAAKF,CAAN,CADW,CAEpBG,QAAQ,CAAE,CAAC,IAAKH,CAAN,CAFU,CAGpBI,QAAQ,CAAE,CAAC,IAAKJ,CAAN,CAHU,CAMtB,CAAO7C,CAAU,CAAA,CAAA,CAAA,CAAA,CAAjB,CAAiB,CAtnCnB,CA+nCiBoD,EAAjBD,MAAiBC,CAxoCnB,CAAA,IA0oCED,CAAAA,CAAM,CAANA,OAAAA,CAAAA,O","sourcesContent":["/* webextension-polyfill - v0.6.0 - Mon Dec 23 2019 12:32:53 */\n/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */\n/* vim: set sts=2 sw=2 et tw=80: */\n/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n\"use strict\";\n\nif (typeof browser === \"undefined\" || Object.getPrototypeOf(browser) !== Object.prototype) {\n const CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE = \"The message port closed before a response was received.\";\n const SEND_RESPONSE_DEPRECATION_WARNING = \"Returning a Promise is the preferred way to send a reply from an onMessage/onMessageExternal listener, as the sendResponse will be removed from the specs (See https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage)\";\n\n // Wrapping the bulk of this polyfill in a one-time-use function is a minor\n // optimization for Firefox. Since Spidermonkey does not fully parse the\n // contents of a function until the first time it's called, and since it will\n // never actually need to be called, this allows the polyfill to be included\n // in Firefox nearly for free.\n const wrapAPIs = extensionAPIs => {\n // NOTE: apiMetadata is associated to the content of the api-metadata.json file\n // at build time by replacing the following \"include\" with the content of the\n // JSON file.\n const apiMetadata = {\n \"alarms\": {\n \"clear\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"clearAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"get\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"bookmarks\": {\n \"create\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"get\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getChildren\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getRecent\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getSubTree\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getTree\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"move\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeTree\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"search\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"update\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n }\n },\n \"browserAction\": {\n \"disable\": {\n \"minArgs\": 0,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"enable\": {\n \"minArgs\": 0,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"getBadgeBackgroundColor\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getBadgeText\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getPopup\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getTitle\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"openPopup\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"setBadgeBackgroundColor\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"setBadgeText\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"setIcon\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"setPopup\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"setTitle\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n }\n },\n \"browsingData\": {\n \"remove\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n },\n \"removeCache\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeCookies\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeDownloads\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeFormData\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeHistory\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeLocalStorage\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removePasswords\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removePluginData\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"settings\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"commands\": {\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"contextMenus\": {\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"update\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n }\n },\n \"cookies\": {\n \"get\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getAll\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getAllCookieStores\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"set\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"devtools\": {\n \"inspectedWindow\": {\n \"eval\": {\n \"minArgs\": 1,\n \"maxArgs\": 2,\n \"singleCallbackArg\": false\n }\n },\n \"panels\": {\n \"create\": {\n \"minArgs\": 3,\n \"maxArgs\": 3,\n \"singleCallbackArg\": true\n }\n }\n },\n \"downloads\": {\n \"cancel\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"download\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"erase\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getFileIcon\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"open\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"pause\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeFile\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"resume\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"search\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"show\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n }\n },\n \"extension\": {\n \"isAllowedFileSchemeAccess\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"isAllowedIncognitoAccess\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"history\": {\n \"addUrl\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"deleteAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"deleteRange\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"deleteUrl\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getVisits\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"search\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"i18n\": {\n \"detectLanguage\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getAcceptLanguages\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"identity\": {\n \"launchWebAuthFlow\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"idle\": {\n \"queryState\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"management\": {\n \"get\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"getSelf\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"setEnabled\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n },\n \"uninstallSelf\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n }\n },\n \"notifications\": {\n \"clear\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"create\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"getPermissionLevel\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"update\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n }\n },\n \"pageAction\": {\n \"getPopup\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getTitle\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"hide\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"setIcon\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"setPopup\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"setTitle\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"show\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n }\n },\n \"permissions\": {\n \"contains\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"request\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"runtime\": {\n \"getBackgroundPage\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"getPlatformInfo\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"openOptionsPage\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"requestUpdateCheck\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"sendMessage\": {\n \"minArgs\": 1,\n \"maxArgs\": 3\n },\n \"sendNativeMessage\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n },\n \"setUninstallURL\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"sessions\": {\n \"getDevices\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getRecentlyClosed\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"restore\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n }\n },\n \"storage\": {\n \"local\": {\n \"clear\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"get\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getBytesInUse\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"set\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"managed\": {\n \"get\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getBytesInUse\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n }\n },\n \"sync\": {\n \"clear\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"get\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getBytesInUse\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"set\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n }\n },\n \"tabs\": {\n \"captureVisibleTab\": {\n \"minArgs\": 0,\n \"maxArgs\": 2\n },\n \"create\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"detectLanguage\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"discard\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"duplicate\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"executeScript\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"get\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getCurrent\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"getZoom\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getZoomSettings\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"highlight\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"insertCSS\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"move\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n },\n \"query\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"reload\": {\n \"minArgs\": 0,\n \"maxArgs\": 2\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeCSS\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"sendMessage\": {\n \"minArgs\": 2,\n \"maxArgs\": 3\n },\n \"setZoom\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"setZoomSettings\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"update\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n }\n },\n \"topSites\": {\n \"get\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"webNavigation\": {\n \"getAllFrames\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getFrame\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"webRequest\": {\n \"handlerBehaviorChanged\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"windows\": {\n \"create\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"get\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getCurrent\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getLastFocused\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"update\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n }\n }\n };\n\n if (Object.keys(apiMetadata).length === 0) {\n throw new Error(\"api-metadata.json has not been included in browser-polyfill\");\n }\n\n /**\n * A WeakMap subclass which creates and stores a value for any key which does\n * not exist when accessed, but behaves exactly as an ordinary WeakMap\n * otherwise.\n *\n * @param {function} createItem\n * A function which will be called in order to create the value for any\n * key which does not exist, the first time it is accessed. The\n * function receives, as its only argument, the key being created.\n */\n class DefaultWeakMap extends WeakMap {\n constructor(createItem, items = undefined) {\n super(items);\n this.createItem = createItem;\n }\n\n get(key) {\n if (!this.has(key)) {\n this.set(key, this.createItem(key));\n }\n\n return super.get(key);\n }\n }\n\n /**\n * Returns true if the given object is an object with a `then` method, and can\n * therefore be assumed to behave as a Promise.\n *\n * @param {*} value The value to test.\n * @returns {boolean} True if the value is thenable.\n */\n const isThenable = value => {\n return value && typeof value === \"object\" && typeof value.then === \"function\";\n };\n\n /**\n * Creates and returns a function which, when called, will resolve or reject\n * the given promise based on how it is called:\n *\n * - If, when called, `chrome.runtime.lastError` contains a non-null object,\n * the promise is rejected with that value.\n * - If the function is called with exactly one argument, the promise is\n * resolved to that value.\n * - Otherwise, the promise is resolved to an array containing all of the\n * function's arguments.\n *\n * @param {object} promise\n * An object containing the resolution and rejection functions of a\n * promise.\n * @param {function} promise.resolve\n * The promise's resolution function.\n * @param {function} promise.rejection\n * The promise's rejection function.\n * @param {object} metadata\n * Metadata about the wrapped method which has created the callback.\n * @param {integer} metadata.maxResolvedArgs\n * The maximum number of arguments which may be passed to the\n * callback created by the wrapped async function.\n *\n * @returns {function}\n * The generated callback function.\n */\n const makeCallback = (promise, metadata) => {\n return (...callbackArgs) => {\n if (extensionAPIs.runtime.lastError) {\n promise.reject(extensionAPIs.runtime.lastError);\n } else if (metadata.singleCallbackArg ||\n (callbackArgs.length <= 1 && metadata.singleCallbackArg !== false)) {\n promise.resolve(callbackArgs[0]);\n } else {\n promise.resolve(callbackArgs);\n }\n };\n };\n\n const pluralizeArguments = (numArgs) => numArgs == 1 ? \"argument\" : \"arguments\";\n\n /**\n * Creates a wrapper function for a method with the given name and metadata.\n *\n * @param {string} name\n * The name of the method which is being wrapped.\n * @param {object} metadata\n * Metadata about the method being wrapped.\n * @param {integer} metadata.minArgs\n * The minimum number of arguments which must be passed to the\n * function. If called with fewer than this number of arguments, the\n * wrapper will raise an exception.\n * @param {integer} metadata.maxArgs\n * The maximum number of arguments which may be passed to the\n * function. If called with more than this number of arguments, the\n * wrapper will raise an exception.\n * @param {integer} metadata.maxResolvedArgs\n * The maximum number of arguments which may be passed to the\n * callback created by the wrapped async function.\n *\n * @returns {function(object, ...*)}\n * The generated wrapper function.\n */\n const wrapAsyncFunction = (name, metadata) => {\n return function asyncFunctionWrapper(target, ...args) {\n if (args.length < metadata.minArgs) {\n throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`);\n }\n\n if (args.length > metadata.maxArgs) {\n throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`);\n }\n\n return new Promise((resolve, reject) => {\n if (metadata.fallbackToNoCallback) {\n // This API method has currently no callback on Chrome, but it return a promise on Firefox,\n // and so the polyfill will try to call it with a callback first, and it will fallback\n // to not passing the callback if the first call fails.\n try {\n target[name](...args, makeCallback({resolve, reject}, metadata));\n } catch (cbError) {\n console.warn(`${name} API method doesn't seem to support the callback parameter, ` +\n \"falling back to call it without a callback: \", cbError);\n\n target[name](...args);\n\n // Update the API method metadata, so that the next API calls will not try to\n // use the unsupported callback anymore.\n metadata.fallbackToNoCallback = false;\n metadata.noCallback = true;\n\n resolve();\n }\n } else if (metadata.noCallback) {\n target[name](...args);\n resolve();\n } else {\n target[name](...args, makeCallback({resolve, reject}, metadata));\n }\n });\n };\n };\n\n /**\n * Wraps an existing method of the target object, so that calls to it are\n * intercepted by the given wrapper function. The wrapper function receives,\n * as its first argument, the original `target` object, followed by each of\n * the arguments passed to the original method.\n *\n * @param {object} target\n * The original target object that the wrapped method belongs to.\n * @param {function} method\n * The method being wrapped. This is used as the target of the Proxy\n * object which is created to wrap the method.\n * @param {function} wrapper\n * The wrapper function which is called in place of a direct invocation\n * of the wrapped method.\n *\n * @returns {Proxy<function>}\n * A Proxy object for the given method, which invokes the given wrapper\n * method in its place.\n */\n const wrapMethod = (target, method, wrapper) => {\n return new Proxy(method, {\n apply(targetMethod, thisObj, args) {\n return wrapper.call(thisObj, target, ...args);\n },\n });\n };\n\n let hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);\n\n /**\n * Wraps an object in a Proxy which intercepts and wraps certain methods\n * based on the given `wrappers` and `metadata` objects.\n *\n * @param {object} target\n * The target object to wrap.\n *\n * @param {object} [wrappers = {}]\n * An object tree containing wrapper functions for special cases. Any\n * function present in this object tree is called in place of the\n * method in the same location in the `target` object tree. These\n * wrapper methods are invoked as described in {@see wrapMethod}.\n *\n * @param {object} [metadata = {}]\n * An object tree containing metadata used to automatically generate\n * Promise-based wrapper functions for asynchronous. Any function in\n * the `target` object tree which has a corresponding metadata object\n * in the same location in the `metadata` tree is replaced with an\n * automatically-generated wrapper function, as described in\n * {@see wrapAsyncFunction}\n *\n * @returns {Proxy<object>}\n */\n const wrapObject = (target, wrappers = {}, metadata = {}) => {\n let cache = Object.create(null);\n let handlers = {\n has(proxyTarget, prop) {\n return prop in target || prop in cache;\n },\n\n get(proxyTarget, prop, receiver) {\n if (prop in cache) {\n return cache[prop];\n }\n\n if (!(prop in target)) {\n return undefined;\n }\n\n let value = target[prop];\n\n if (typeof value === \"function\") {\n // This is a method on the underlying object. Check if we need to do\n // any wrapping.\n\n if (typeof wrappers[prop] === \"function\") {\n // We have a special-case wrapper for this method.\n value = wrapMethod(target, target[prop], wrappers[prop]);\n } else if (hasOwnProperty(metadata, prop)) {\n // This is an async method that we have metadata for. Create a\n // Promise wrapper for it.\n let wrapper = wrapAsyncFunction(prop, metadata[prop]);\n value = wrapMethod(target, target[prop], wrapper);\n } else {\n // This is a method that we don't know or care about. Return the\n // original method, bound to the underlying object.\n value = value.bind(target);\n }\n } else if (typeof value === \"object\" && value !== null &&\n (hasOwnProperty(wrappers, prop) ||\n hasOwnProperty(metadata, prop))) {\n // This is an object that we need to do some wrapping for the children\n // of. Create a sub-object wrapper for it with the appropriate child\n // metadata.\n value = wrapObject(value, wrappers[prop], metadata[prop]);\n } else if (hasOwnProperty(metadata, \"*\")) {\n // Wrap all properties in * namespace.\n value = wrapObject(value, wrappers[prop], metadata[\"*\"]);\n } else {\n // We don't need to do any wrapping for this property,\n // so just forward all access to the underlying object.\n Object.defineProperty(cache, prop, {\n configurable: true,\n enumerable: true,\n get() {\n return target[prop];\n },\n set(value) {\n target[prop] = value;\n },\n });\n\n return value;\n }\n\n cache[prop] = value;\n return value;\n },\n\n set(proxyTarget, prop, value, receiver) {\n if (prop in cache) {\n cache[prop] = value;\n } else {\n target[prop] = value;\n }\n return true;\n },\n\n defineProperty(proxyTarget, prop, desc) {\n return Reflect.defineProperty(cache, prop, desc);\n },\n\n deleteProperty(proxyTarget, prop) {\n return Reflect.deleteProperty(cache, prop);\n },\n };\n\n // Per contract of the Proxy API, the \"get\" proxy handler must return the\n // original value of the target if that value is declared read-only and\n // non-configurable. For this reason, we create an object with the\n // prototype set to `target` instead of using `target` directly.\n // Otherwise we cannot return a custom object for APIs that\n // are declared read-only and non-configurable, such as `chrome.devtools`.\n //\n // The proxy handlers themselves will still use the original `target`\n // instead of the `proxyTarget`, so that the methods and properties are\n // dereferenced via the original targets.\n let proxyTarget = Object.create(target);\n return new Proxy(proxyTarget, handlers);\n };\n\n /**\n * Creates a set of wrapper functions for an event object, which handles\n * wrapping of listener functions that those messages are passed.\n *\n * A single wrapper is created for each listener function, and stored in a\n * map. Subsequent calls to `addListener`, `hasListener`, or `removeListener`\n * retrieve the original wrapper, so that attempts to remove a\n * previously-added listener work as expected.\n *\n * @param {DefaultWeakMap<function, function>} wrapperMap\n * A DefaultWeakMap object which will create the appropriate wrapper\n * for a given listener function when one does not exist, and retrieve\n * an existing one when it does.\n *\n * @returns {object}\n */\n const wrapEvent = wrapperMap => ({\n addListener(target, listener, ...args) {\n target.addListener(wrapperMap.get(listener), ...args);\n },\n\n hasListener(target, listener) {\n return target.hasListener(wrapperMap.get(listener));\n },\n\n removeListener(target, listener) {\n target.removeListener(wrapperMap.get(listener));\n },\n });\n\n // Keep track if the deprecation warning has been logged at least once.\n let loggedSendResponseDeprecationWarning = false;\n\n const onMessageWrappers = new DefaultWeakMap(listener => {\n if (typeof listener !== \"function\") {\n return listener;\n }\n\n /**\n * Wraps a message listener function so that it may send responses based on\n * its return value, rather than by returning a sentinel value and calling a\n * callback. If the listener function returns a Promise, the response is\n * sent when the promise either resolves or rejects.\n *\n * @param {*} message\n * The message sent by the other end of the channel.\n * @param {object} sender\n * Details about the sender of the message.\n * @param {function(*)} sendResponse\n * A callback which, when called with an arbitrary argument, sends\n * that value as a response.\n * @returns {boolean}\n * True if the wrapped listener returned a Promise, which will later\n * yield a response. False otherwise.\n */\n return function onMessage(message, sender, sendResponse) {\n let didCallSendResponse = false;\n\n let wrappedSendResponse;\n let sendResponsePromise = new Promise(resolve => {\n wrappedSendResponse = function(response) {\n if (!loggedSendResponseDeprecationWarning) {\n console.warn(SEND_RESPONSE_DEPRECATION_WARNING, new Error().stack);\n loggedSendResponseDeprecationWarning = true;\n }\n didCallSendResponse = true;\n resolve(response);\n };\n });\n\n let result;\n try {\n result = listener(message, sender, wrappedSendResponse);\n } catch (err) {\n result = Promise.reject(err);\n }\n\n const isResultThenable = result !== true && isThenable(result);\n\n // If the listener didn't returned true or a Promise, or called\n // wrappedSendResponse synchronously, we can exit earlier\n // because there will be no response sent from this listener.\n if (result !== true && !isResultThenable && !didCallSendResponse) {\n return false;\n }\n\n // A small helper to send the message if the promise resolves\n // and an error if the promise rejects (a wrapped sendMessage has\n // to translate the message into a resolved promise or a rejected\n // promise).\n const sendPromisedResult = (promise) => {\n promise.then(msg => {\n // send the message value.\n sendResponse(msg);\n }, error => {\n // Send a JSON representation of the error if the rejected value\n // is an instance of error, or the object itself otherwise.\n let message;\n if (error && (error instanceof Error ||\n typeof error.message === \"string\")) {\n message = error.message;\n } else {\n message = \"An unexpected error occurred\";\n }\n\n sendResponse({\n __mozWebExtensionPolyfillReject__: true,\n message,\n });\n }).catch(err => {\n // Print an error on the console if unable to send the response.\n console.error(\"Failed to send onMessage rejected reply\", err);\n });\n };\n\n // If the listener returned a Promise, send the resolved value as a\n // result, otherwise wait the promise related to the wrappedSendResponse\n // callback to resolve and send it as a response.\n if (isResultThenable) {\n sendPromisedResult(result);\n } else {\n sendPromisedResult(sendResponsePromise);\n }\n\n // Let Chrome know that the listener is replying.\n return true;\n };\n });\n\n const wrappedSendMessageCallback = ({reject, resolve}, reply) => {\n if (extensionAPIs.runtime.lastError) {\n // Detect when none of the listeners replied to the sendMessage call and resolve\n // the promise to undefined as in Firefox.\n // See https://github.com/mozilla/webextension-polyfill/issues/130\n if (extensionAPIs.runtime.lastError.message === CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE) {\n resolve();\n } else {\n reject(extensionAPIs.runtime.lastError);\n }\n } else if (reply && reply.__mozWebExtensionPolyfillReject__) {\n // Convert back the JSON representation of the error into\n // an Error instance.\n reject(new Error(reply.message));\n } else {\n resolve(reply);\n }\n };\n\n const wrappedSendMessage = (name, metadata, apiNamespaceObj, ...args) => {\n if (args.length < metadata.minArgs) {\n throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`);\n }\n\n if (args.length > metadata.maxArgs) {\n throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`);\n }\n\n return new Promise((resolve, reject) => {\n const wrappedCb = wrappedSendMessageCallback.bind(null, {resolve, reject});\n args.push(wrappedCb);\n apiNamespaceObj.sendMessage(...args);\n });\n };\n\n const staticWrappers = {\n runtime: {\n onMessage: wrapEvent(onMessageWrappers),\n onMessageExternal: wrapEvent(onMessageWrappers),\n sendMessage: wrappedSendMessage.bind(null, \"sendMessage\", {minArgs: 1, maxArgs: 3}),\n },\n tabs: {\n sendMessage: wrappedSendMessage.bind(null, \"sendMessage\", {minArgs: 2, maxArgs: 3}),\n },\n };\n const settingMetadata = {\n clear: {minArgs: 1, maxArgs: 1},\n get: {minArgs: 1, maxArgs: 1},\n set: {minArgs: 1, maxArgs: 1},\n };\n apiMetadata.privacy = {\n network: {\"*\": settingMetadata},\n services: {\"*\": settingMetadata},\n websites: {\"*\": settingMetadata},\n };\n\n return wrapObject(extensionAPIs, staticWrappers, apiMetadata);\n };\n\n if (typeof chrome != \"object\" || !chrome || !chrome.runtime || !chrome.runtime.id) {\n throw new Error(\"This script should only be loaded in a browser extension.\");\n }\n\n // The build process adds a UMD wrapper around this file, which makes the\n // `module` variable available.\n module.exports = wrapAPIs(chrome);\n} else {\n module.exports = browser;\n}\n"],"file":"browser-polyfill.min.js"} ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/js/popup.js�����������������������������������������0000664�0000000�0000000�00000011426�14215434441�0024342�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict'; var CONNECTED = false; var DEVICES = []; var TARGET_URL = null; // Suppress errors caused by Mozilla polyfill // TODO: not sure if these are relevant anymore const _MUTE = [ 'Could not establish connection. Receiving end does not exist.', 'The message port closed before a response was received.', ]; // Simple error logging function // eslint-disable-next-line no-redeclare function logError(error) { if (!_MUTE.includes(error.message)) console.error(error.message); } /** * Share a URL, either direct to the browser or by SMS * * @param {string} device - The deviceId * @param {string} action - Currently either 'share' or 'telephony' * @param {string} url - The URL to share */ async function sendUrl(device, action, url) { try { window.close(); await browser.runtime.sendMessage({ type: 'share', data: { device: device, url: url, action: action, }, }); } catch (e) { logError(e); } } /** * Create and return a device element for the popup menu * * @param {object} device - A JSON object describing a connected device * @return {HTMLElement} - A <div> element with icon, name and actions */ function getDeviceElement(device) { const deviceElement = document.createElement('div'); deviceElement.className = 'device'; const deviceIcon = document.createElement('img'); deviceIcon.className = 'device-icon'; deviceIcon.src = `images/${device.type}.svg`; deviceElement.appendChild(deviceIcon); const deviceName = document.createElement('span'); deviceName.className = 'device-name'; deviceName.textContent = device.name; deviceElement.appendChild(deviceName); if (device.share) { const shareButton = document.createElement('img'); shareButton.className = 'plugin-button'; shareButton.src = 'images/open-in-browser.svg'; shareButton.title = browser.i18n.getMessage('shareMessage'); shareButton.addEventListener( 'click', () => sendUrl(device.id, 'share', URL) ); deviceElement.appendChild(shareButton); } if (device.telephony) { const telephonyButton = document.createElement('img'); telephonyButton.className = 'plugin-button'; telephonyButton.src = 'images/message.svg'; telephonyButton.title = browser.i18n.getMessage('smsMessage'); telephonyButton.addEventListener( 'click', () => sendUrl(device.id, 'telephony', URL) ); deviceElement.appendChild(telephonyButton); } return deviceElement; } /** * Populate the browserAction popup */ function setPopup() { const devNode = document.getElementById('popup'); while (devNode.hasChildNodes()) devNode.removeChild(devNode.lastChild); if (CONNECTED && DEVICES.length) { for (const device of DEVICES) { const deviceElement = getDeviceElement(device); devNode.appendChild(deviceElement); } return; } // Disconnected or no devices const message = document.createElement('span'); message.className = 'popup-menu-message'; devNode.appendChild(message); // The native-messaging-host or service is disconnected if (!CONNECTED) message.textContent = browser.i18n.getMessage('popupMenuDisconnected'); // There are no devices else message.textContent = browser.i18n.getMessage('popupMenuNoDevices'); } /** * Callback for receiving a message forwarded by background.js * * @param {Object} message - A JSON message object * @param {runtime.MessageSender} sender - The sender of the message. */ function onPortMessage(message, sender) { try { // console.log(`WebExtension-popup RECV: ${JSON.stringify(message)}`); if (sender.url.includes('/background.html')) { if (message.type === 'connected') { CONNECTED = message.data; } else if (message.type === 'devices') { CONNECTED = true; DEVICES = message.data; } setPopup(); } } catch (e) { logError(e); } } /** * Set the current URL and repopulate the popup, on-demand */ async function onPopup() { try { const tabs = await browser.tabs.query({ active: true, currentWindow: true, }); if (tabs.length) TARGET_URL = tabs[0].url; setPopup(); await browser.runtime.sendMessage({type: 'devices'}); } catch (e) { logError(e); } } /** * Startup: listen for forwarded messages and populate the popup on-demand */ browser.runtime.onMessage.addListener(onPortMessage); document.addEventListener('DOMContentLoaded', onPopup); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/manifest.chrome.json��������������������������������0000664�0000000�0000000�00000002166�14215434441�0026203�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "manifest_version": 2, "name": "__MSG_extensionName__", "description": "__MSG_extensionDescription__", "version": "8", "homepage_url": "https://github.com/GSConnect/gnome-shell-extension-gsconnect/wiki", "default_locale": "en", "browser_action": { "default_title": "__MSG_extensionName__", "default_popup": "popup.html", "default_icon": { "256": "images/gsconnect-256.png", "128": "images/gsconnect-128.png", "64": "images/gsconnect-64.png", "48": "images/gsconnect-48.png", "32": "images/gsconnect-32.png", "24": "images/gsconnect-24.png", "22": "images/gsconnect-22.png", "16": "images/gsconnect-16.png" } }, "background": { "page": "background.html" }, "permissions": [ "nativeMessaging", "tabs", "contextMenus" ], "icons": { "256": "images/gsconnect-256.png", "128": "images/gsconnect-128.png", "64": "images/gsconnect-64.png", "48": "images/gsconnect-48.png", "32": "images/gsconnect-32.png", "24": "images/gsconnect-24.png", "22": "images/gsconnect-22.png", "16": "images/gsconnect-16.png" } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/manifest.firefox.json�������������������������������0000664�0000000�0000000�00000001613�14215434441�0026364�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "manifest_version": 2, "name": "__MSG_extensionName__", "description": "__MSG_extensionDescription__", "version": "8", "homepage_url": "https://github.com/GSConnect/gnome-shell-extension-gsconnect/wiki", "default_locale": "en", "browser_action": { "default_title": "__MSG_extensionName__", "default_popup": "popup.html", "default_icon": "images/gsconnect.svg" }, "applications": { "gecko": { "id": "gsconnect@andyholmes.github.io" } }, "background": { "page": "background.html" }, "permissions": [ "nativeMessaging", "tabs", "contextMenus" ], "icons": { "256": "images/gsconnect.svg", "128": "images/gsconnect.svg", "64": "images/gsconnect.svg", "48": "images/gsconnect.svg", "32": "images/gsconnect.svg", "24": "images/gsconnect.svg", "22": "images/gsconnect.svg", "16": "images/gsconnect.svg" } } ���������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/mkwebext.sh�����������������������������������������0000775�0000000�0000000�00000003657�14215434441�0024421�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # A script for building GSConnect WebExtension zips for Chrome or Firefox. # TODO: Mozilla Firefox extension requires node 'web-ext' # Update translations if [ "${1}" == "i18n" ]; then echo -n "Updating translations..." ./gettext.js echo "done" exit # Common preparation for chrome & firefox elif [ "${1}" == "chrome" ] || [ "${1}" == "firefox" ]; then # Clean-up old files rm -rf ${1}.zip # Copy relevant files mkdir ${1} mkdir ${1}/images cp -R js ${1}/ cp -R _locales ${1}/ cp background.html ${1}/ cp manifest.${1}.json ${1}/manifest.json cp popup.html ${1}/ cp stylesheet.css ${1}/ fi # Build Mozilla Firefox Add-on if [ "${1}" == "firefox" ]; then echo -n "Building Mozilla Firefox Add-on..." # Firefox only needs SVG cp -R images/*.svg ${1}/images # Make the ZIP ~/node_modules/.bin/web-ext -s ${1} build > /dev/null 2>&1 mv web-ext-artifacts/gsconnect-*.zip ${1}.zip # Cleanup rm -rf ${1} web-ext-artifacts echo "done" exit # Build Google Chrome/Chromium Extension elif [ "${1}" == "chrome" ]; then echo -n "Building Google Chrome/Chromium Extension..." # Remove Firefox-only features sed -i '/FIREFOX-ONLY/{N;d}' ${1}/js/background.js sed -i '/FIREFOX-ONLY/{N;d}' ${1}/js/popup.js # Chrome needs SVG and PNG cp -R images/* ${1}/images/ # Make the ZIP cd chrome/ zip -r ../chrome.zip * > /dev/null 2>&1 # Cleanup cd .. rm -rf ${1} echo "done" exit fi # Usage echo "Usage: mkwebext [firefox|chrome|i18n]" echo "Build an unsigned ZIP of the WebExtension for Chrome or Firefox." echo echo " chrome Build Google Chrome/Chromium Extension (unsigned zip)" echo " firefox Build Mozilla Firefox Add-on zip (unsigned zip)" echo " i18n Update translations" echo "" echo "Building the Mozilla Firefox extension requires the 'web-ext' node module." exit 1 ���������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/popup.html������������������������������������������0000664�0000000�0000000�00000000471�14215434441�0024254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE html> <html> <head> <link rel="stylesheet" href="stylesheet.css" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <div id="popup"/> <script src="js/browser-polyfill.min.js"></script> <script src="js/popup.js"></script> </body> </html> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gnome-shell-extension-gsconnect-50/webextension/stylesheet.css��������������������������������������0000664�0000000�0000000�00000001361�14215434441�0025125�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������html, body { margin: 0; padding: 0; background-color: #fafafa; font-family: Ubuntu, Cantrell, Arial, sans-serif; font-size: 0.9em; } #popup { display: flex; flex-direction: column; justify-content: center; min-width: 15em; margin: 0.5em 0; } .device { white-space: nowrap; display: flex; align-items: center; } .device-icon { margin: 0.5em; } .device-name { margin-right: auto; } .plugin-button { border-radius: 3px; margin-right: 6px; padding: 4px; } .plugin-button:hover { background-color: rgba(204, 204, 204, 0.5); } .plugin-button:active { background-color: rgba(204, 204, 204, 0.75); } .popup-menu-message { font-style: italic; text-align: center; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������