diff --git a/muk_web_preview_vector/LICENSE b/muk_web_preview_vector/LICENSE new file mode 100644 index 0000000..faf7bf4 --- /dev/null +++ b/muk_web_preview_vector/LICENSE @@ -0,0 +1,619 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + 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 +them 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. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero 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 that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/muk_web_preview_vector/README.md b/muk_web_preview_vector/README.md new file mode 100644 index 0000000..107d688 --- /dev/null +++ b/muk_web_preview_vector/README.md @@ -0,0 +1,6 @@ +# MuK Preview Vector + +Extendes the Preview Dialog to support vector graphics. +Currently the following vector graphic extensions are supported: + +* Scalable Vector Graphics (*.svg, image/svg+xml) \ No newline at end of file diff --git a/muk_web_preview_vector/__init__.py b/muk_web_preview_vector/__init__.py new file mode 100644 index 0000000..48ccf85 --- /dev/null +++ b/muk_web_preview_vector/__init__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- + +################################################################################### +# +# Copyright (C) 2017 MuK IT GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### diff --git a/muk_web_preview_vector/__manifest__.py b/muk_web_preview_vector/__manifest__.py new file mode 100644 index 0000000..09481e0 --- /dev/null +++ b/muk_web_preview_vector/__manifest__.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +################################################################################### +# +# Copyright (C) 2017 MuK IT GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### + +{ + "name": "MuK Preview Vector", + "summary": """Vector Preview""", + "description": """ + Extendes the Preview Dialog to support vector graphics. + Currently the following vector graphic extensions are supported: + - Scalable Vector Graphics (*.svg, image/svg+xml) + """, + "version": "10.0.1.0.0", + "category": "Extra Tools", + "license": "AGPL-3", + "website": "http://www.mukit.at", + "author": "MuK IT", + "contributors": [ + "Mathias Markl ", + ], + "depends": [ + "muk_web_preview", + ], + "data": [ + "template/assets.xml", + ], + "demo": [ + ], + "qweb": [ + "static/src/xml/*.xml", + ], + "images": [ + 'static/description/banner.png' + ], + "external_dependencies": { + "python": [], + "bin": [], + }, + "application": False, + "installable": True, + +} \ No newline at end of file diff --git a/muk_web_preview_vector/static/description/banner.png b/muk_web_preview_vector/static/description/banner.png new file mode 100644 index 0000000..6ff88bb Binary files /dev/null and b/muk_web_preview_vector/static/description/banner.png differ diff --git a/muk_web_preview_vector/static/description/icon.png b/muk_web_preview_vector/static/description/icon.png new file mode 100644 index 0000000..b6fef0d Binary files /dev/null and b/muk_web_preview_vector/static/description/icon.png differ diff --git a/muk_web_preview_vector/static/description/index.html b/muk_web_preview_vector/static/description/index.html new file mode 100644 index 0000000..ee4fb29 --- /dev/null +++ b/muk_web_preview_vector/static/description/index.html @@ -0,0 +1,40 @@ +
+
+

Preview Vector

+

Preview your vector graphics directly in Odoo.

+

MuK IT GmbH - + www.mukit.at

+
+ +
+
+
+ +
+
+
+

Overview

+

Extendes the Preview Dialog to support vector + graphics. Currently the following vector graphic extensions are + supported:

+

+ Scalable Vector + Graphics (*.svg, image/svg+xml) +

+
+
+
+ +
+

Help and Support

+ + +
\ No newline at end of file diff --git a/muk_web_preview_vector/static/description/logo.png b/muk_web_preview_vector/static/description/logo.png new file mode 100644 index 0000000..9427ce3 Binary files /dev/null and b/muk_web_preview_vector/static/description/logo.png differ diff --git a/muk_web_preview_vector/static/description/screenshot.png b/muk_web_preview_vector/static/description/screenshot.png new file mode 100644 index 0000000..9c9be73 Binary files /dev/null and b/muk_web_preview_vector/static/description/screenshot.png differ diff --git a/muk_web_preview_vector/static/lib/svg-pan-zoom/svg-pan-zoom.js b/muk_web_preview_vector/static/lib/svg-pan-zoom/svg-pan-zoom.js new file mode 100644 index 0000000..f556bc6 --- /dev/null +++ b/muk_web_preview_vector/static/lib/svg-pan-zoom/svg-pan-zoom.js @@ -0,0 +1,1902 @@ +// svg-pan-zoom v3.5.1 +// https://github.com/ariutta/svg-pan-zoom +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0; i--) { + if (this.eventListeners.hasOwnProperty(haltEventListeners[i])) { + delete this.eventListeners[haltEventListeners[i]] + } + } + } + } + + // Bind eventListeners + for (var event in this.eventListeners) { + // Attach event to eventsListenerElement or SVG if not available + (this.options.eventsListenerElement || this.svg) + .addEventListener(event, this.eventListeners[event], false) + } + + // Zoom using mouse wheel + if (this.options.mouseWheelZoomEnabled) { + this.options.mouseWheelZoomEnabled = false // set to false as enable will set it back to true + this.enableMouseWheelZoom() + } +} + +/** + * Enable ability to zoom using mouse wheel + */ +SvgPanZoom.prototype.enableMouseWheelZoom = function() { + if (!this.options.mouseWheelZoomEnabled) { + var that = this + + // Mouse wheel listener + this.wheelListener = function(evt) { + return that.handleMouseWheel(evt); + } + + // Bind wheelListener + Wheel.on(this.options.eventsListenerElement || this.svg, this.wheelListener, false) + + this.options.mouseWheelZoomEnabled = true + } +} + +/** + * Disable ability to zoom using mouse wheel + */ +SvgPanZoom.prototype.disableMouseWheelZoom = function() { + if (this.options.mouseWheelZoomEnabled) { + Wheel.off(this.options.eventsListenerElement || this.svg, this.wheelListener, false) + this.options.mouseWheelZoomEnabled = false + } +} + +/** + * Handle mouse wheel event + * + * @param {Event} evt + */ +SvgPanZoom.prototype.handleMouseWheel = function(evt) { + if (!this.options.zoomEnabled || this.state !== 'none') { + return; + } + + if (this.options.preventMouseEventsDefault){ + if (evt.preventDefault) { + evt.preventDefault(); + } else { + evt.returnValue = false; + } + } + + // Default delta in case that deltaY is not available + var delta = evt.deltaY || 1 + , timeDelta = Date.now() - this.lastMouseWheelEventTime + , divider = 3 + Math.max(0, 30 - timeDelta) + + // Update cache + this.lastMouseWheelEventTime = Date.now() + + // Make empirical adjustments for browsers that give deltaY in pixels (deltaMode=0) + if ('deltaMode' in evt && evt.deltaMode === 0 && evt.wheelDelta) { + delta = evt.deltaY === 0 ? 0 : Math.abs(evt.wheelDelta) / evt.deltaY + } + + delta = -0.3 < delta && delta < 0.3 ? delta : (delta > 0 ? 1 : -1) * Math.log(Math.abs(delta) + 10) / divider + + var inversedScreenCTM = this.svg.getScreenCTM().inverse() + , relativeMousePoint = SvgUtils.getEventPoint(evt, this.svg).matrixTransform(inversedScreenCTM) + , zoom = Math.pow(1 + this.options.zoomScaleSensitivity, (-1) * delta); // multiplying by neg. 1 so as to make zoom in/out behavior match Google maps behavior + + this.zoomAtPoint(zoom, relativeMousePoint) +} + +/** + * Zoom in at a SVG point + * + * @param {SVGPoint} point + * @param {Float} zoomScale Number representing how much to zoom + * @param {Boolean} zoomAbsolute Default false. If true, zoomScale is treated as an absolute value. + * Otherwise, zoomScale is treated as a multiplied (e.g. 1.10 would zoom in 10%) + */ +SvgPanZoom.prototype.zoomAtPoint = function(zoomScale, point, zoomAbsolute) { + var originalState = this.viewport.getOriginalState() + + if (!zoomAbsolute) { + // Fit zoomScale in set bounds + if (this.getZoom() * zoomScale < this.options.minZoom * originalState.zoom) { + zoomScale = (this.options.minZoom * originalState.zoom) / this.getZoom() + } else if (this.getZoom() * zoomScale > this.options.maxZoom * originalState.zoom) { + zoomScale = (this.options.maxZoom * originalState.zoom) / this.getZoom() + } + } else { + // Fit zoomScale in set bounds + zoomScale = Math.max(this.options.minZoom * originalState.zoom, Math.min(this.options.maxZoom * originalState.zoom, zoomScale)) + // Find relative scale to achieve desired scale + zoomScale = zoomScale/this.getZoom() + } + + var oldCTM = this.viewport.getCTM() + , relativePoint = point.matrixTransform(oldCTM.inverse()) + , modifier = this.svg.createSVGMatrix().translate(relativePoint.x, relativePoint.y).scale(zoomScale).translate(-relativePoint.x, -relativePoint.y) + , newCTM = oldCTM.multiply(modifier) + + if (newCTM.a !== oldCTM.a) { + this.viewport.setCTM(newCTM) + } +} + +/** + * Zoom at center point + * + * @param {Float} scale + * @param {Boolean} absolute Marks zoom scale as relative or absolute + */ +SvgPanZoom.prototype.zoom = function(scale, absolute) { + this.zoomAtPoint(scale, SvgUtils.getSvgCenterPoint(this.svg, this.width, this.height), absolute) +} + +/** + * Zoom used by public instance + * + * @param {Float} scale + * @param {Boolean} absolute Marks zoom scale as relative or absolute + */ +SvgPanZoom.prototype.publicZoom = function(scale, absolute) { + if (absolute) { + scale = this.computeFromRelativeZoom(scale) + } + + this.zoom(scale, absolute) +} + +/** + * Zoom at point used by public instance + * + * @param {Float} scale + * @param {SVGPoint|Object} point An object that has x and y attributes + * @param {Boolean} absolute Marks zoom scale as relative or absolute + */ +SvgPanZoom.prototype.publicZoomAtPoint = function(scale, point, absolute) { + if (absolute) { + // Transform zoom into a relative value + scale = this.computeFromRelativeZoom(scale) + } + + // If not a SVGPoint but has x and y then create a SVGPoint + if (Utils.getType(point) !== 'SVGPoint') { + if('x' in point && 'y' in point) { + point = SvgUtils.createSVGPoint(this.svg, point.x, point.y) + } else { + throw new Error('Given point is invalid') + } + } + + this.zoomAtPoint(scale, point, absolute) +} + +/** + * Get zoom scale + * + * @return {Float} zoom scale + */ +SvgPanZoom.prototype.getZoom = function() { + return this.viewport.getZoom() +} + +/** + * Get zoom scale for public usage + * + * @return {Float} zoom scale + */ +SvgPanZoom.prototype.getRelativeZoom = function() { + return this.viewport.getRelativeZoom() +} + +/** + * Compute actual zoom from public zoom + * + * @param {Float} zoom + * @return {Float} zoom scale + */ +SvgPanZoom.prototype.computeFromRelativeZoom = function(zoom) { + return zoom * this.viewport.getOriginalState().zoom +} + +/** + * Set zoom to initial state + */ +SvgPanZoom.prototype.resetZoom = function() { + var originalState = this.viewport.getOriginalState() + + this.zoom(originalState.zoom, true); +} + +/** + * Set pan to initial state + */ +SvgPanZoom.prototype.resetPan = function() { + this.pan(this.viewport.getOriginalState()); +} + +/** + * Set pan and zoom to initial state + */ +SvgPanZoom.prototype.reset = function() { + this.resetZoom() + this.resetPan() +} + +/** + * Handle double click event + * See handleMouseDown() for alternate detection method + * + * @param {Event} evt + */ +SvgPanZoom.prototype.handleDblClick = function(evt) { + if (this.options.preventMouseEventsDefault) { + if (evt.preventDefault) { + evt.preventDefault() + } else { + evt.returnValue = false + } + } + + // Check if target was a control button + if (this.options.controlIconsEnabled) { + var targetClass = evt.target.getAttribute('class') || '' + if (targetClass.indexOf('svg-pan-zoom-control') > -1) { + return false + } + } + + var zoomFactor + + if (evt.shiftKey) { + zoomFactor = 1/((1 + this.options.zoomScaleSensitivity) * 2) // zoom out when shift key pressed + } else { + zoomFactor = (1 + this.options.zoomScaleSensitivity) * 2 + } + + var point = SvgUtils.getEventPoint(evt, this.svg).matrixTransform(this.svg.getScreenCTM().inverse()) + this.zoomAtPoint(zoomFactor, point) +} + +/** + * Handle click event + * + * @param {Event} evt + */ +SvgPanZoom.prototype.handleMouseDown = function(evt, prevEvt) { + if (this.options.preventMouseEventsDefault) { + if (evt.preventDefault) { + evt.preventDefault() + } else { + evt.returnValue = false + } + } + + Utils.mouseAndTouchNormalize(evt, this.svg) + + // Double click detection; more consistent than ondblclick + if (this.options.dblClickZoomEnabled && Utils.isDblClick(evt, prevEvt)){ + this.handleDblClick(evt) + } else { + // Pan mode + this.state = 'pan' + this.firstEventCTM = this.viewport.getCTM() + this.stateOrigin = SvgUtils.getEventPoint(evt, this.svg).matrixTransform(this.firstEventCTM.inverse()) + } +} + +/** + * Handle mouse move event + * + * @param {Event} evt + */ +SvgPanZoom.prototype.handleMouseMove = function(evt) { + if (this.options.preventMouseEventsDefault) { + if (evt.preventDefault) { + evt.preventDefault() + } else { + evt.returnValue = false + } + } + + if (this.state === 'pan' && this.options.panEnabled) { + // Pan mode + var point = SvgUtils.getEventPoint(evt, this.svg).matrixTransform(this.firstEventCTM.inverse()) + , viewportCTM = this.firstEventCTM.translate(point.x - this.stateOrigin.x, point.y - this.stateOrigin.y) + + this.viewport.setCTM(viewportCTM) + } +} + +/** + * Handle mouse button release event + * + * @param {Event} evt + */ +SvgPanZoom.prototype.handleMouseUp = function(evt) { + if (this.options.preventMouseEventsDefault) { + if (evt.preventDefault) { + evt.preventDefault() + } else { + evt.returnValue = false + } + } + + if (this.state === 'pan') { + // Quit pan mode + this.state = 'none' + } +} + +/** + * Adjust viewport size (only) so it will fit in SVG + * Does not center image + */ +SvgPanZoom.prototype.fit = function() { + var viewBox = this.viewport.getViewBox() + , newScale = Math.min(this.width/viewBox.width, this.height/viewBox.height) + + this.zoom(newScale, true) +} + +/** + * Adjust viewport size (only) so it will contain the SVG + * Does not center image + */ +SvgPanZoom.prototype.contain = function() { + var viewBox = this.viewport.getViewBox() + , newScale = Math.max(this.width/viewBox.width, this.height/viewBox.height) + + this.zoom(newScale, true) +} + +/** + * Adjust viewport pan (only) so it will be centered in SVG + * Does not zoom/fit/contain image + */ +SvgPanZoom.prototype.center = function() { + var viewBox = this.viewport.getViewBox() + , offsetX = (this.width - (viewBox.width + viewBox.x * 2) * this.getZoom()) * 0.5 + , offsetY = (this.height - (viewBox.height + viewBox.y * 2) * this.getZoom()) * 0.5 + + this.getPublicInstance().pan({x: offsetX, y: offsetY}) +} + +/** + * Update content cached BorderBox + * Use when viewport contents change + */ +SvgPanZoom.prototype.updateBBox = function() { + this.viewport.simpleViewBoxCache() +} + +/** + * Pan to a rendered position + * + * @param {Object} point {x: 0, y: 0} + */ +SvgPanZoom.prototype.pan = function(point) { + var viewportCTM = this.viewport.getCTM() + viewportCTM.e = point.x + viewportCTM.f = point.y + this.viewport.setCTM(viewportCTM) +} + +/** + * Relatively pan the graph by a specified rendered position vector + * + * @param {Object} point {x: 0, y: 0} + */ +SvgPanZoom.prototype.panBy = function(point) { + var viewportCTM = this.viewport.getCTM() + viewportCTM.e += point.x + viewportCTM.f += point.y + this.viewport.setCTM(viewportCTM) +} + +/** + * Get pan vector + * + * @return {Object} {x: 0, y: 0} + */ +SvgPanZoom.prototype.getPan = function() { + var state = this.viewport.getState() + + return {x: state.x, y: state.y} +} + +/** + * Recalculates cached svg dimensions and controls position + */ +SvgPanZoom.prototype.resize = function() { + // Get dimensions + var boundingClientRectNormalized = SvgUtils.getBoundingClientRectNormalized(this.svg) + this.width = boundingClientRectNormalized.width + this.height = boundingClientRectNormalized.height + + // Recalculate original state + var viewport = this.viewport + viewport.options.width = this.width + viewport.options.height = this.height + viewport.processCTM() + + // Reposition control icons by re-enabling them + if (this.options.controlIconsEnabled) { + this.getPublicInstance().disableControlIcons() + this.getPublicInstance().enableControlIcons() + } +} + +/** + * Unbind mouse events, free callbacks and destroy public instance + */ +SvgPanZoom.prototype.destroy = function() { + var that = this + + // Free callbacks + this.beforeZoom = null + this.onZoom = null + this.beforePan = null + this.onPan = null + this.onUpdatedCTM = null + + // Destroy custom event handlers + if (this.options.customEventsHandler != null) { // jshint ignore:line + this.options.customEventsHandler.destroy({ + svgElement: this.svg + , eventsListenerElement: this.options.eventsListenerElement + , instance: this.getPublicInstance() + }) + } + + // Unbind eventListeners + for (var event in this.eventListeners) { + (this.options.eventsListenerElement || this.svg) + .removeEventListener(event, this.eventListeners[event], false) + } + + // Unbind wheelListener + this.disableMouseWheelZoom() + + // Remove control icons + this.getPublicInstance().disableControlIcons() + + // Reset zoom and pan + this.reset() + + // Remove instance from instancesStore + instancesStore = instancesStore.filter(function(instance){ + return instance.svg !== that.svg + }) + + // Delete options and its contents + delete this.options + + // Delete viewport to make public shadow viewport functions uncallable + delete this.viewport + + // Destroy public instance and rewrite getPublicInstance + delete this.publicInstance + delete this.pi + this.getPublicInstance = function(){ + return null + } +} + +/** + * Returns a public instance object + * + * @return {Object} Public instance object + */ +SvgPanZoom.prototype.getPublicInstance = function() { + var that = this + + // Create cache + if (!this.publicInstance) { + this.publicInstance = this.pi = { + // Pan + enablePan: function() {that.options.panEnabled = true; return that.pi} + , disablePan: function() {that.options.panEnabled = false; return that.pi} + , isPanEnabled: function() {return !!that.options.panEnabled} + , pan: function(point) {that.pan(point); return that.pi} + , panBy: function(point) {that.panBy(point); return that.pi} + , getPan: function() {return that.getPan()} + // Pan event + , setBeforePan: function(fn) {that.options.beforePan = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi} + , setOnPan: function(fn) {that.options.onPan = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi} + // Zoom and Control Icons + , enableZoom: function() {that.options.zoomEnabled = true; return that.pi} + , disableZoom: function() {that.options.zoomEnabled = false; return that.pi} + , isZoomEnabled: function() {return !!that.options.zoomEnabled} + , enableControlIcons: function() { + if (!that.options.controlIconsEnabled) { + that.options.controlIconsEnabled = true + ControlIcons.enable(that) + } + return that.pi + } + , disableControlIcons: function() { + if (that.options.controlIconsEnabled) { + that.options.controlIconsEnabled = false; + ControlIcons.disable(that) + } + return that.pi + } + , isControlIconsEnabled: function() {return !!that.options.controlIconsEnabled} + // Double click zoom + , enableDblClickZoom: function() {that.options.dblClickZoomEnabled = true; return that.pi} + , disableDblClickZoom: function() {that.options.dblClickZoomEnabled = false; return that.pi} + , isDblClickZoomEnabled: function() {return !!that.options.dblClickZoomEnabled} + // Mouse wheel zoom + , enableMouseWheelZoom: function() {that.enableMouseWheelZoom(); return that.pi} + , disableMouseWheelZoom: function() {that.disableMouseWheelZoom(); return that.pi} + , isMouseWheelZoomEnabled: function() {return !!that.options.mouseWheelZoomEnabled} + // Zoom scale and bounds + , setZoomScaleSensitivity: function(scale) {that.options.zoomScaleSensitivity = scale; return that.pi} + , setMinZoom: function(zoom) {that.options.minZoom = zoom; return that.pi} + , setMaxZoom: function(zoom) {that.options.maxZoom = zoom; return that.pi} + // Zoom event + , setBeforeZoom: function(fn) {that.options.beforeZoom = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi} + , setOnZoom: function(fn) {that.options.onZoom = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi} + // Zooming + , zoom: function(scale) {that.publicZoom(scale, true); return that.pi} + , zoomBy: function(scale) {that.publicZoom(scale, false); return that.pi} + , zoomAtPoint: function(scale, point) {that.publicZoomAtPoint(scale, point, true); return that.pi} + , zoomAtPointBy: function(scale, point) {that.publicZoomAtPoint(scale, point, false); return that.pi} + , zoomIn: function() {this.zoomBy(1 + that.options.zoomScaleSensitivity); return that.pi} + , zoomOut: function() {this.zoomBy(1 / (1 + that.options.zoomScaleSensitivity)); return that.pi} + , getZoom: function() {return that.getRelativeZoom()} + // CTM update + , setOnUpdatedCTM: function(fn) {that.options.onUpdatedCTM = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi} + // Reset + , resetZoom: function() {that.resetZoom(); return that.pi} + , resetPan: function() {that.resetPan(); return that.pi} + , reset: function() {that.reset(); return that.pi} + // Fit, Contain and Center + , fit: function() {that.fit(); return that.pi} + , contain: function() {that.contain(); return that.pi} + , center: function() {that.center(); return that.pi} + // Size and Resize + , updateBBox: function() {that.updateBBox(); return that.pi} + , resize: function() {that.resize(); return that.pi} + , getSizes: function() { + return { + width: that.width + , height: that.height + , realZoom: that.getZoom() + , viewBox: that.viewport.getViewBox() + } + } + // Destroy + , destroy: function() {that.destroy(); return that.pi} + } + } + + return this.publicInstance +} + +/** + * Stores pairs of instances of SvgPanZoom and SVG + * Each pair is represented by an object {svg: SVGSVGElement, instance: SvgPanZoom} + * + * @type {Array} + */ +var instancesStore = [] + +var svgPanZoom = function(elementOrSelector, options){ + var svg = Utils.getSvg(elementOrSelector) + + if (svg === null) { + return null + } else { + // Look for existent instance + for(var i = instancesStore.length - 1; i >= 0; i--) { + if (instancesStore[i].svg === svg) { + return instancesStore[i].instance.getPublicInstance() + } + } + + // If instance not found - create one + instancesStore.push({ + svg: svg + , instance: new SvgPanZoom(svg, options) + }) + + // Return just pushed instance + return instancesStore[instancesStore.length - 1].instance.getPublicInstance() + } +} + +module.exports = svgPanZoom; + +},{"./control-icons":2,"./shadow-viewport":3,"./svg-utilities":5,"./uniwheel":6,"./utilities":7}],5:[function(require,module,exports){ +var Utils = require('./utilities') + , _browser = 'unknown' + ; + +// http://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser +if (/*@cc_on!@*/false || !!document.documentMode) { // internet explorer + _browser = 'ie'; +} + +module.exports = { + svgNS: 'http://www.w3.org/2000/svg' +, xmlNS: 'http://www.w3.org/XML/1998/namespace' +, xmlnsNS: 'http://www.w3.org/2000/xmlns/' +, xlinkNS: 'http://www.w3.org/1999/xlink' +, evNS: 'http://www.w3.org/2001/xml-events' + + /** + * Get svg dimensions: width and height + * + * @param {SVGSVGElement} svg + * @return {Object} {width: 0, height: 0} + */ +, getBoundingClientRectNormalized: function(svg) { + if (svg.clientWidth && svg.clientHeight) { + return {width: svg.clientWidth, height: svg.clientHeight} + } else if (!!svg.getBoundingClientRect()) { + return svg.getBoundingClientRect(); + } else { + throw new Error('Cannot get BoundingClientRect for SVG.'); + } + } + + /** + * Gets g element with class of "viewport" or creates it if it doesn't exist + * + * @param {SVGSVGElement} svg + * @return {SVGElement} g (group) element + */ +, getOrCreateViewport: function(svg, selector) { + var viewport = null + + if (Utils.isElement(selector)) { + viewport = selector + } else { + viewport = svg.querySelector(selector) + } + + // Check if there is just one main group in SVG + if (!viewport) { + var childNodes = Array.prototype.slice.call(svg.childNodes || svg.children).filter(function(el){ + return el.nodeName !== 'defs' && el.nodeName !== '#text' + }) + + // Node name should be SVGGElement and should have no transform attribute + // Groups with transform are not used as viewport because it involves parsing of all transform possibilities + if (childNodes.length === 1 && childNodes[0].nodeName === 'g' && childNodes[0].getAttribute('transform') === null) { + viewport = childNodes[0] + } + } + + // If no favorable group element exists then create one + if (!viewport) { + var viewportId = 'viewport-' + new Date().toISOString().replace(/\D/g, ''); + viewport = document.createElementNS(this.svgNS, 'g'); + viewport.setAttribute('id', viewportId); + + // Internet Explorer (all versions?) can't use childNodes, but other browsers prefer (require?) using childNodes + var svgChildren = svg.childNodes || svg.children; + if (!!svgChildren && svgChildren.length > 0) { + for (var i = svgChildren.length; i > 0; i--) { + // Move everything into viewport except defs + if (svgChildren[svgChildren.length - i].nodeName !== 'defs') { + viewport.appendChild(svgChildren[svgChildren.length - i]); + } + } + } + svg.appendChild(viewport); + } + + // Parse class names + var classNames = []; + if (viewport.getAttribute('class')) { + classNames = viewport.getAttribute('class').split(' ') + } + + // Set class (if not set already) + if (!~classNames.indexOf('svg-pan-zoom_viewport')) { + classNames.push('svg-pan-zoom_viewport') + viewport.setAttribute('class', classNames.join(' ')) + } + + return viewport + } + + /** + * Set SVG attributes + * + * @param {SVGSVGElement} svg + */ + , setupSvgAttributes: function(svg) { + // Setting default attributes + svg.setAttribute('xmlns', this.svgNS); + svg.setAttributeNS(this.xmlnsNS, 'xmlns:xlink', this.xlinkNS); + svg.setAttributeNS(this.xmlnsNS, 'xmlns:ev', this.evNS); + + // Needed for Internet Explorer, otherwise the viewport overflows + if (svg.parentNode !== null) { + var style = svg.getAttribute('style') || ''; + if (style.toLowerCase().indexOf('overflow') === -1) { + svg.setAttribute('style', 'overflow: hidden; ' + style); + } + } + } + +/** + * How long Internet Explorer takes to finish updating its display (ms). + */ +, internetExplorerRedisplayInterval: 300 + +/** + * Forces the browser to redisplay all SVG elements that rely on an + * element defined in a 'defs' section. It works globally, for every + * available defs element on the page. + * The throttling is intentionally global. + * + * This is only needed for IE. It is as a hack to make markers (and 'use' elements?) + * visible after pan/zoom when there are multiple SVGs on the page. + * See bug report: https://connect.microsoft.com/IE/feedback/details/781964/ + * also see svg-pan-zoom issue: https://github.com/ariutta/svg-pan-zoom/issues/62 + */ +, refreshDefsGlobal: Utils.throttle(function() { + var allDefs = document.querySelectorAll('defs'); + var allDefsCount = allDefs.length; + for (var i = 0; i < allDefsCount; i++) { + var thisDefs = allDefs[i]; + thisDefs.parentNode.insertBefore(thisDefs, thisDefs); + } + }, this.internetExplorerRedisplayInterval) + + /** + * Sets the current transform matrix of an element + * + * @param {SVGElement} element + * @param {SVGMatrix} matrix CTM + * @param {SVGElement} defs + */ +, setCTM: function(element, matrix, defs) { + var that = this + , s = 'matrix(' + matrix.a + ',' + matrix.b + ',' + matrix.c + ',' + matrix.d + ',' + matrix.e + ',' + matrix.f + ')'; + + element.setAttributeNS(null, 'transform', s); + if ('transform' in element.style) { + element.style.transform = s; + } else if ('-ms-transform' in element.style) { + element.style['-ms-transform'] = s; + } else if ('-webkit-transform' in element.style) { + element.style['-webkit-transform'] = s; + } + + // IE has a bug that makes markers disappear on zoom (when the matrix "a" and/or "d" elements change) + // see http://stackoverflow.com/questions/17654578/svg-marker-does-not-work-in-ie9-10 + // and http://srndolha.wordpress.com/2013/11/25/svg-line-markers-may-disappear-in-internet-explorer-11/ + if (_browser === 'ie' && !!defs) { + // this refresh is intended for redisplaying the SVG during zooming + defs.parentNode.insertBefore(defs, defs); + // this refresh is intended for redisplaying the other SVGs on a page when panning a given SVG + // it is also needed for the given SVG itself, on zoomEnd, if the SVG contains any markers that + // are located under any other element(s). + window.setTimeout(function() { + that.refreshDefsGlobal(); + }, that.internetExplorerRedisplayInterval); + } + } + + /** + * Instantiate an SVGPoint object with given event coordinates + * + * @param {Event} evt + * @param {SVGSVGElement} svg + * @return {SVGPoint} point + */ +, getEventPoint: function(evt, svg) { + var point = svg.createSVGPoint() + + Utils.mouseAndTouchNormalize(evt, svg) + + point.x = evt.clientX + point.y = evt.clientY + + return point + } + + /** + * Get SVG center point + * + * @param {SVGSVGElement} svg + * @return {SVGPoint} + */ +, getSvgCenterPoint: function(svg, width, height) { + return this.createSVGPoint(svg, width / 2, height / 2) + } + + /** + * Create a SVGPoint with given x and y + * + * @param {SVGSVGElement} svg + * @param {Number} x + * @param {Number} y + * @return {SVGPoint} + */ +, createSVGPoint: function(svg, x, y) { + var point = svg.createSVGPoint() + point.x = x + point.y = y + + return point + } +} + +},{"./utilities":7}],6:[function(require,module,exports){ +// uniwheel 0.1.2 (customized) +// A unified cross browser mouse wheel event handler +// https://github.com/teemualap/uniwheel + +module.exports = (function(){ + + //Full details: https://developer.mozilla.org/en-US/docs/Web/Reference/Events/wheel + + var prefix = "", _addEventListener, _removeEventListener, onwheel, support, fns = []; + + // detect event model + if ( window.addEventListener ) { + _addEventListener = "addEventListener"; + _removeEventListener = "removeEventListener"; + } else { + _addEventListener = "attachEvent"; + _removeEventListener = "detachEvent"; + prefix = "on"; + } + + // detect available wheel event + support = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel" + document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel" + "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox + + + function createCallback(element,callback,capture) { + + var fn = function(originalEvent) { + + !originalEvent && ( originalEvent = window.event ); + + // create a normalized event object + var event = { + // keep a ref to the original event object + originalEvent: originalEvent, + target: originalEvent.target || originalEvent.srcElement, + type: "wheel", + deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1, + deltaX: 0, + delatZ: 0, + preventDefault: function() { + originalEvent.preventDefault ? + originalEvent.preventDefault() : + originalEvent.returnValue = false; + } + }; + + // calculate deltaY (and deltaX) according to the event + if ( support == "mousewheel" ) { + event.deltaY = - 1/40 * originalEvent.wheelDelta; + // Webkit also support wheelDeltaX + originalEvent.wheelDeltaX && ( event.deltaX = - 1/40 * originalEvent.wheelDeltaX ); + } else { + event.deltaY = originalEvent.detail; + } + + // it's time to fire the callback + return callback( event ); + + }; + + fns.push({ + element: element, + fn: fn, + capture: capture + }); + + return fn; + } + + function getCallback(element,capture) { + for (var i = 0; i < fns.length; i++) { + if (fns[i].element === element && fns[i].capture === capture) { + return fns[i].fn; + } + } + return function(){}; + } + + function removeCallback(element,capture) { + for (var i = 0; i < fns.length; i++) { + if (fns[i].element === element && fns[i].capture === capture) { + return fns.splice(i,1); + } + } + } + + function _addWheelListener( elem, eventName, callback, useCapture ) { + + var cb; + + if (support === "wheel") { + cb = callback; + } else { + cb = createCallback(elem,callback,useCapture); + } + + elem[ _addEventListener ]( prefix + eventName, cb, useCapture || false ); + + } + + function _removeWheelListener( elem, eventName, callback, useCapture ) { + + var cb; + + if (support === "wheel") { + cb = callback; + } else { + cb = getCallback(elem,useCapture); + } + + elem[ _removeEventListener ]( prefix + eventName, cb, useCapture || false ); + + removeCallback(elem,useCapture); + + } + + function addWheelListener( elem, callback, useCapture ) { + _addWheelListener( elem, support, callback, useCapture ); + + // handle MozMousePixelScroll in older Firefox + if( support == "DOMMouseScroll" ) { + _addWheelListener( elem, "MozMousePixelScroll", callback, useCapture); + } + } + + function removeWheelListener(elem,callback,useCapture){ + _removeWheelListener(elem,support,callback,useCapture); + + // handle MozMousePixelScroll in older Firefox + if( support == "DOMMouseScroll" ) { + _removeWheelListener(elem, "MozMousePixelScroll", callback, useCapture); + } + } + + return { + on: addWheelListener, + off: removeWheelListener + }; + +})(); + +},{}],7:[function(require,module,exports){ +module.exports = { + /** + * Extends an object + * + * @param {Object} target object to extend + * @param {Object} source object to take properties from + * @return {Object} extended object + */ + extend: function(target, source) { + target = target || {}; + for (var prop in source) { + // Go recursively + if (this.isObject(source[prop])) { + target[prop] = this.extend(target[prop], source[prop]) + } else { + target[prop] = source[prop] + } + } + return target; + } + + /** + * Checks if an object is a DOM element + * + * @param {Object} o HTML element or String + * @return {Boolean} returns true if object is a DOM element + */ +, isElement: function(o){ + return ( + o instanceof HTMLElement || o instanceof SVGElement || o instanceof SVGSVGElement || //DOM2 + (o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName === 'string') + ); + } + + /** + * Checks if an object is an Object + * + * @param {Object} o Object + * @return {Boolean} returns true if object is an Object + */ +, isObject: function(o){ + return Object.prototype.toString.call(o) === '[object Object]'; + } + + /** + * Checks if variable is Number + * + * @param {Integer|Float} n + * @return {Boolean} returns true if variable is Number + */ +, isNumber: function(n) { + return !isNaN(parseFloat(n)) && isFinite(n); + } + + /** + * Search for an SVG element + * + * @param {Object|String} elementOrSelector DOM Element or selector String + * @return {Object|Null} SVG or null + */ +, getSvg: function(elementOrSelector) { + var element + , svg; + + if (!this.isElement(elementOrSelector)) { + // If selector provided + if (typeof elementOrSelector === 'string' || elementOrSelector instanceof String) { + // Try to find the element + element = document.querySelector(elementOrSelector) + + if (!element) { + throw new Error('Provided selector did not find any elements. Selector: ' + elementOrSelector) + return null + } + } else { + throw new Error('Provided selector is not an HTML object nor String') + return null + } + } else { + element = elementOrSelector + } + + if (element.tagName.toLowerCase() === 'svg') { + svg = element; + } else { + if (element.tagName.toLowerCase() === 'object') { + svg = element.contentDocument.documentElement; + } else { + if (element.tagName.toLowerCase() === 'embed') { + svg = element.getSVGDocument().documentElement; + } else { + if (element.tagName.toLowerCase() === 'img') { + throw new Error('Cannot script an SVG in an "img" element. Please use an "object" element or an in-line SVG.'); + } else { + throw new Error('Cannot get SVG.'); + } + return null + } + } + } + + return svg + } + + /** + * Attach a given context to a function + * @param {Function} fn Function + * @param {Object} context Context + * @return {Function} Function with certain context + */ +, proxy: function(fn, context) { + return function() { + return fn.apply(context, arguments) + } + } + + /** + * Returns object type + * Uses toString that returns [object SVGPoint] + * And than parses object type from string + * + * @param {Object} o Any object + * @return {String} Object type + */ +, getType: function(o) { + return Object.prototype.toString.apply(o).replace(/^\[object\s/, '').replace(/\]$/, '') + } + + /** + * If it is a touch event than add clientX and clientY to event object + * + * @param {Event} evt + * @param {SVGSVGElement} svg + */ +, mouseAndTouchNormalize: function(evt, svg) { + // If no cilentX and but touch objects are available + if (evt.clientX === void 0 || evt.clientX === null) { + // Fallback + evt.clientX = 0 + evt.clientY = 0 + + // If it is a touch event + if (evt.changedTouches !== void 0 && evt.changedTouches.length) { + // If touch event has changedTouches + if (evt.changedTouches[0].clientX !== void 0) { + evt.clientX = evt.changedTouches[0].clientX + evt.clientY = evt.changedTouches[0].clientY + } + // If changedTouches has pageX attribute + else if (evt.changedTouches[0].pageX !== void 0) { + var rect = svg.getBoundingClientRect(); + + evt.clientX = evt.changedTouches[0].pageX - rect.left + evt.clientY = evt.changedTouches[0].pageY - rect.top + } + // If it is a custom event + } else if (evt.originalEvent !== void 0) { + if (evt.originalEvent.clientX !== void 0) { + evt.clientX = evt.originalEvent.clientX + evt.clientY = evt.originalEvent.clientY + } + } + } + } + + /** + * Check if an event is a double click/tap + * TODO: For touch gestures use a library (hammer.js) that takes in account other events + * (touchmove and touchend). It should take in account tap duration and traveled distance + * + * @param {Event} evt + * @param {Event} prevEvt Previous Event + * @return {Boolean} + */ +, isDblClick: function(evt, prevEvt) { + // Double click detected by browser + if (evt.detail === 2) { + return true; + } + // Try to compare events + else if (prevEvt !== void 0 && prevEvt !== null) { + var timeStampDiff = evt.timeStamp - prevEvt.timeStamp // should be lower than 250 ms + , touchesDistance = Math.sqrt(Math.pow(evt.clientX - prevEvt.clientX, 2) + Math.pow(evt.clientY - prevEvt.clientY, 2)) + + return timeStampDiff < 250 && touchesDistance < 10 + } + + // Nothing found + return false; + } + + /** + * Returns current timestamp as an integer + * + * @return {Number} + */ +, now: Date.now || function() { + return new Date().getTime(); + } + + // From underscore. + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. Normally, the throttled function will run + // as much as it can, without ever going more than once per `wait` duration; + // but if you'd like to disable the execution on the leading edge, pass + // `{leading: false}`. To disable execution on the trailing edge, ditto. +// jscs:disable +// jshint ignore:start +, throttle: function(func, wait, options) { + var that = this; + var context, args, result; + var timeout = null; + var previous = 0; + if (!options) options = {}; + var later = function() { + previous = options.leading === false ? 0 : that.now(); + timeout = null; + result = func.apply(context, args); + if (!timeout) context = args = null; + }; + return function() { + var now = that.now(); + if (!previous && options.leading === false) previous = now; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0 || remaining > wait) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + if (!timeout) context = args = null; + } else if (!timeout && options.trailing !== false) { + timeout = setTimeout(later, remaining); + } + return result; + }; + } +// jshint ignore:end +// jscs:enable + + /** + * Create a requestAnimationFrame simulation + * + * @param {Number|String} refreshRate + * @return {Function} + */ +, createRequestAnimationFrame: function(refreshRate) { + var timeout = null + + // Convert refreshRate to timeout + if (refreshRate !== 'auto' && refreshRate < 60 && refreshRate > 1) { + timeout = Math.floor(1000 / refreshRate) + } + + if (timeout === null) { + return window.requestAnimationFrame || requestTimeout(33) + } else { + return requestTimeout(timeout) + } + } +} + +/** + * Create a callback that will execute after a given timeout + * + * @param {Function} timeout + * @return {Function} + */ +function requestTimeout(timeout) { + return function(callback) { + window.setTimeout(callback, timeout) + } +} + +},{}]},{},[1]); \ No newline at end of file diff --git a/muk_web_preview_vector/static/src/js/preview_generator.js b/muk_web_preview_vector/static/src/js/preview_generator.js new file mode 100644 index 0000000..bd00b63 --- /dev/null +++ b/muk_web_preview_vector/static/src/js/preview_generator.js @@ -0,0 +1,73 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_preview_vector.PreviewGenerator', function (require) { +"use strict"; + +var core = require('web.core'); + +var PreviewHandler = require('muk_preview.PreviewHandler'); +var PreviewGenerator = require('muk_preview.PreviewGenerator'); + +var QWeb = core.qweb; +var _t = core._t; + +var VectorHandler = PreviewHandler.extend({ + checkExtension: function(extension) { + return ['.svg', 'svg'].includes(extension); + }, + checkType: function(mimetype) { + return ['image/svg+xml'].includes(mimetype); + }, + createHtml: function(url, mimetype, extension, title) { + var result = $.Deferred(); + var $content = $(QWeb.render('VectorHTMLContent', {url: url})); + $.ajax(url, { + dataType: "text", + success: function(vector) { + $content.find('.vector-loader').hide(); + $content.find('.vector-container').show(); + $content.find('.vector-container').html(vector); + svgPanZoom('svg', { + zoomEnabled: true, + controlIconsEnabled: true, + fit: true, + center: true, + minZoom: 0.1 + }); + }, + error: function(request, status, error) { + console.error(request.responseText); + } + }); + result.resolve($content); + return $.when(result); + }, +}); + +PreviewGenerator.include({ + init: function(widget, additional_handler) { + this._super(widget, additional_handler); + this.handler = _.extend(this.handler, { + "VectorHandler": new VectorHandler(widget), + }); + }, +}); + +}); \ No newline at end of file diff --git a/muk_web_preview_vector/static/src/less/preview_content.less b/muk_web_preview_vector/static/src/less/preview_content.less new file mode 100644 index 0000000..b329e3b --- /dev/null +++ b/muk_web_preview_vector/static/src/less/preview_content.less @@ -0,0 +1,143 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +.o_preview_vector { + position: absolute; + width: 100%; + height: 100%; + + .vector-container { + display: none; + + svg { + position: absolute; + width: 100%; + height: 100%; + } + } + + /* Based on a CodePen Snippet by Dave McCarthy */ + .vector-loader { + height: 20px; + width: 250px; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: auto; + + .loader--dot { + animation-name: loader; + animation-timing-function: ease-in-out; + animation-duration: 3s; + animation-iteration-count: infinite; + height: 20px; + width: 20px; + border-radius: 100%; + background-color: black; + position: absolute; + border: 2px solid white; + } + + .loader--dot:first-child { + background-color: #8cc759; + animation-delay: 0.5s; + } + + .loader--dot:nth-child(2) { + background-color: #8c6daf; + animation-delay: 0.4s; + } + + .loader--dot:nth-child(3) { + background-color: #ef5d74; + animation-delay: 0.3s; + } + + .loader--dot:nth-child(4) { + background-color: #f9a74b; + animation-delay: 0.2s; + } + + .loader--dot:nth-child(5) { + background-color: #60beeb; + animation-delay: 0.1s; + } + + .loader--dot:nth-child(6) { + background-color: #fbef5a; + animation-delay: 0s; + } + + .loader--text { + position: absolute; + top: 200%; + left: 0; + right: 0; + width: 4rem; + margin: auto; + } + + .loader--text:after { + content: "Loading"; + font-weight: bold; + animation-name: loading-text; + animation-duration: 3s; + animation-iteration-count: infinite; + } + + @keyframes loader { + 15% { + transform: translateX(0); + } + + 45% { + transform: translateX(230px); + } + + 65% { + transform: translateX(230px); + } + + 95% { + transform: translateX(0); + } + } + + @keyframes loading-text { + 0% { + content: "Loading"; + } + + 25% { + content: "Loading."; + } + + 50% { + content: "Loading.."; + } + + 75% { + content: "Loading..."; + } + } + } +} + diff --git a/muk_web_preview_vector/static/src/xml/preview_content.xml b/muk_web_preview_vector/static/src/xml/preview_content.xml new file mode 100644 index 0000000..a53f6cd --- /dev/null +++ b/muk_web_preview_vector/static/src/xml/preview_content.xml @@ -0,0 +1,33 @@ + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ \ No newline at end of file diff --git a/muk_web_preview_vector/template/assets.xml b/muk_web_preview_vector/template/assets.xml new file mode 100644 index 0000000..c801ad4 --- /dev/null +++ b/muk_web_preview_vector/template/assets.xml @@ -0,0 +1,29 @@ + + + + + +