Kaynağa Gözat

Update Wodo.TextEditor

Angel Fernando Quiroz Campos 7 yıl önce
ebeveyn
işleme
9977835a07
35 değiştirilmiş dosya ile 2981 ekleme ve 1516 silme
  1. 661 0
      main/inc/lib/javascript/wodotexteditor/AGPL-3.0.txt
  2. 38 39
      main/inc/lib/javascript/wodotexteditor/EditorSession.js
  3. 1 2
      main/inc/lib/javascript/wodotexteditor/FullWindowZoomHelper.js
  4. 149 0
      main/inc/lib/javascript/wodotexteditor/HOWTO.md
  5. 39 0
      main/inc/lib/javascript/wodotexteditor/README.md
  6. 97 50
      main/inc/lib/javascript/wodotexteditor/Tools.js
  7. 5 1
      main/inc/lib/javascript/wodotexteditor/Translator.js
  8. 511 501
      main/inc/lib/javascript/wodotexteditor/dojo-amalgamation.js
  9. BIN
      main/inc/lib/javascript/wodotexteditor/images/saveas.png
  10. 33 21
      main/inc/lib/javascript/wodotexteditor/localfileeditor.js
  11. 189 0
      main/inc/lib/javascript/wodotexteditor/revieweditor.js
  12. 2 2
      main/inc/lib/javascript/wodotexteditor/translations/de-DE.json
  13. 64 0
      main/inc/lib/javascript/wodotexteditor/translations/eu.json
  14. 66 0
      main/inc/lib/javascript/wodotexteditor/translations/fr-FR.json
  15. 0 2
      main/inc/lib/javascript/wodotexteditor/translations/it-IT.json
  16. 2 2
      main/inc/lib/javascript/wodotexteditor/translations/nl-NL.json
  17. 615 594
      main/inc/lib/javascript/wodotexteditor/webodf.js
  18. 11 9
      main/inc/lib/javascript/wodotexteditor/widgets/aboutDialog.js
  19. 4 3
      main/inc/lib/javascript/wodotexteditor/widgets/annotation.js
  20. 19 16
      main/inc/lib/javascript/wodotexteditor/widgets/dialogWidgets/alignmentPane.js
  21. 9 6
      main/inc/lib/javascript/wodotexteditor/widgets/dialogWidgets/editHyperlinkPane.js
  22. 18 13
      main/inc/lib/javascript/wodotexteditor/widgets/dialogWidgets/fontEffectsPane.js
  23. 90 0
      main/inc/lib/javascript/wodotexteditor/widgets/dialogWidgets/idMangler.js
  24. 29 42
      main/inc/lib/javascript/wodotexteditor/widgets/editHyperlinks.js
  25. 10 4
      main/inc/lib/javascript/wodotexteditor/widgets/fontPicker.js
  26. 4 2
      main/inc/lib/javascript/wodotexteditor/widgets/imageInserter.js
  27. 6 5
      main/inc/lib/javascript/wodotexteditor/widgets/paragraphAlignment.js
  28. 14 6
      main/inc/lib/javascript/wodotexteditor/widgets/paragraphStyles.js
  29. 20 17
      main/inc/lib/javascript/wodotexteditor/widgets/paragraphStylesDialog.js
  30. 17 23
      main/inc/lib/javascript/wodotexteditor/widgets/simpleStyles.js
  31. 47 46
      main/inc/lib/javascript/wodotexteditor/widgets/toolbarWidgets/currentStyle.js
  32. 4 2
      main/inc/lib/javascript/wodotexteditor/widgets/undoRedoMenu.js
  33. 55 55
      main/inc/lib/javascript/wodotexteditor/widgets/zoomSlider.js
  34. 17 0
      main/inc/lib/javascript/wodotexteditor/wodotexteditor.css
  35. 135 53
      main/inc/lib/javascript/wodotexteditor/wodotexteditor.js

+ 661 - 0
main/inc/lib/javascript/wodotexteditor/AGPL-3.0.txt

@@ -0,0 +1,661 @@
+                    GNU AFFERO GENERAL PUBLIC LICENSE
+                       Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ 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
+
+            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
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source.  For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code.  There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.

+ 38 - 39
main/inc/lib/javascript/wodotexteditor/EditorSession.js

@@ -22,7 +22,7 @@
  * @source: https://github.com/kogmbh/WebODF/
  */
 
-/*global define, runtime, core, gui, ops, document */
+/*global runtime, define, document, core, odf, gui, ops*/
 
 define("webodf/editor/EditorSession", [
     "dojo/text!resources/fonts/fonts.css"
@@ -40,6 +40,7 @@ define("webodf/editor/EditorSession", [
     runtime.loadClass("odf.OdfUtils");
     runtime.loadClass("gui.CaretManager");
     runtime.loadClass("gui.Caret");
+    runtime.loadClass("gui.OdfFieldView");
     runtime.loadClass("gui.SessionController");
     runtime.loadClass("gui.SessionView");
     runtime.loadClass("gui.HyperlinkTooltipView");
@@ -52,10 +53,11 @@ define("webodf/editor/EditorSession", [
 
     /**
      * Instantiate a new editor session attached to an existing operation session
+     * @constructor
+     * @implements {core.EventSource}
      * @param {!ops.Session} session
      * @param {!string} localMemberId
      * @param {{viewOptions:gui.SessionViewOptions,directParagraphStylingEnabled:boolean,annotationsEnabled:boolean}} config
-     * @constructor
      */
     var EditorSession = function EditorSession(session, localMemberId, config) {
         var self = this,
@@ -69,8 +71,9 @@ define("webodf/editor/EditorSession", [
             textns = odf.Namespaces.textns,
             fontStyles = document.createElement('style'),
             formatting = odtDocument.getFormatting(),
-            domUtils = new core.DomUtils(),
-            odfUtils = new odf.OdfUtils(),
+            domUtils = core.DomUtils,
+            odfUtils = odf.OdfUtils,
+            odfFieldView,
             eventNotifier = new core.EventNotifier([
                 EditorSession.signalMemberAdded,
                 EditorSession.signalMemberUpdated,
@@ -84,7 +87,9 @@ define("webodf/editor/EditorSession", [
                 EditorSession.signalParagraphStyleModified,
                 EditorSession.signalUndoStackChanged]),
             shadowCursor = new gui.ShadowCursor(odtDocument),
-            sessionConstraints;
+            sessionConstraints,
+            /**@const*/
+            NEXT = core.StepDirection.NEXT;
 
         /**
          * @return {Array.<!string>}
@@ -94,7 +99,9 @@ define("webodf/editor/EditorSession", [
 
             availableFonts = {};
 
+            /*jslint regexp: true*/
             regex =  /font-family *: *(?:\'([^']*)\'|\"([^"]*)\")/gm;
+            /*jslint regexp: false*/
             matches = regex.exec(fontsCSS);
 
             while (matches) {
@@ -148,7 +155,7 @@ define("webodf/editor/EditorSession", [
                 i;
 
             // encode
-            for (i = 0; i < name.length; i++) {
+            for (i = 0; i < name.length; i += 1) {
                 letter = name[i];
                 // simple approach, can be improved to not skip other allowed chars
                 if (letter.match(/[a-zA-Z0-9.-_]/) !== null) {
@@ -177,7 +184,7 @@ define("webodf/editor/EditorSession", [
             // then loop until result is really unique
             while (formatting.hasParagraphStyle(result)) {
                 result = ncName + "_" + i + "_" + ncMemberId;
-                i++;
+                i += 1;
             }
 
             return result;
@@ -186,7 +193,7 @@ define("webodf/editor/EditorSession", [
         function trackCursor(cursor) {
             var node;
 
-            node = odtDocument.getParagraphElement(cursor.getNode());
+            node = odfUtils.getParagraphElement(cursor.getNode());
             if (!node) {
                 return;
             }
@@ -298,15 +305,6 @@ define("webodf/editor/EditorSession", [
             return currentCommonStyleName;
         };
 
-        /**
-         * Round the step up to the next step
-         * @param {!number} step
-         * @return {!boolean}
-         */
-        function roundUp(step) {
-            return step === ops.OdtStepsTranslator.NEXT_STEP;
-        }
-
         /**
          * Applies the paragraph style with the given
          * style name to all the paragraphs within
@@ -320,7 +318,7 @@ define("webodf/editor/EditorSession", [
                 opQueue = [];
 
             paragraphs.forEach(function (paragraph) {
-                var paragraphStartPoint = odtDocument.convertDomPointToCursorStep(paragraph, 0, roundUp),
+                var paragraphStartPoint = odtDocument.convertDomPointToCursorStep(paragraph, 0, NEXT),
                     paragraphStyleName = paragraph.getAttributeNS(odf.Namespaces.textns, "style-name"),
                     opSetParagraphStyle;
 
@@ -359,13 +357,15 @@ define("webodf/editor/EditorSession", [
          * element. If the style name is an empty string, the default style
          * is returned.
          * @param {!string} styleName
-         * @return {Element}
+         * @return {?Element}
          */
-        this.getParagraphStyleElement = function (styleName) {
+        function getParagraphStyleElement(styleName) {
             return (styleName === "")
                 ? formatting.getDefaultStyleElement('paragraph')
-                : odtDocument.getParagraphStyleElement(styleName);
-        };
+                : formatting.getStyleElement(styleName, 'paragraph');
+        }
+
+        this.getParagraphStyleElement = getParagraphStyleElement;
 
         /**
          * Returns if the style is used anywhere in the document
@@ -376,26 +376,22 @@ define("webodf/editor/EditorSession", [
             return formatting.isStyleUsed(styleElement);
         };
 
-        function getDefaultParagraphStyleAttributes() {
-            var styleNode = formatting.getDefaultStyleElement('paragraph');
-            if (styleNode) {
-                return formatting.getInheritedStyleAttributes(styleNode);
-            }
-
-            return null;
-        }
-
         /**
          * Returns the attributes of a given paragraph style name
          * (with inheritance). If the name is an empty string,
          * the attributes of the default style are returned.
          * @param {!string} styleName
-         * @return {Object}
+         * @return {?odf.Formatting.StyleData}
          */
         this.getParagraphStyleAttributes = function (styleName) {
-            return (styleName === "")
-                ? getDefaultParagraphStyleAttributes()
-                : odtDocument.getParagraphStyleAttributes(styleName);
+            var styleNode = getParagraphStyleElement(styleName),
+                includeSystemDefault = styleName === "";
+
+            if (styleNode) {
+                return formatting.getInheritedStyleAttributes(styleNode, includeSystemDefault);
+            }
+
+            return null;
         };
 
         /**
@@ -427,8 +423,7 @@ define("webodf/editor/EditorSession", [
          */
         this.cloneParagraphStyle = function (styleName, newStyleDisplayName) {
             var newStyleName = uniqueParagraphStyleNCName(newStyleDisplayName),
-                styleNode = self.getParagraphStyleElement(styleName),
-                formatting = odtDocument.getFormatting(),
+                styleNode = getParagraphStyleElement(styleName),
                 op, setProperties, attributes, i;
 
             setProperties = formatting.getStyleAttributes(styleNode);
@@ -585,6 +580,7 @@ define("webodf/editor/EditorSession", [
                     selectionViewManager.destroy,
                     self.sessionController.destroy,
                     hyperlinkTooltipView.destroy,
+                    odfFieldView.destroy,
                     destroy
                 ];
 
@@ -593,6 +589,7 @@ define("webodf/editor/EditorSession", [
 
         function init() {
             var head = document.getElementsByTagName('head')[0],
+                odfCanvas = session.getOdtDocument().getOdfCanvas(),
                 eventManager;
 
             // TODO: fonts.css should be rather done by odfCanvas, or?
@@ -601,6 +598,8 @@ define("webodf/editor/EditorSession", [
             fontStyles.appendChild(document.createTextNode(fontsCSS));
             head.appendChild(fontStyles);
 
+            odfFieldView = new gui.OdfFieldView(odfCanvas);
+            odfFieldView.showFieldHighlight();
             self.sessionController = new gui.SessionController(session, localMemberId, shadowCursor, {
                 annotationsEnabled: config.annotationsEnabled,
                 directTextStylingEnabled: config.directTextStylingEnabled,
@@ -609,12 +608,12 @@ define("webodf/editor/EditorSession", [
             sessionConstraints = self.sessionController.getSessionConstraints();
 
             eventManager = self.sessionController.getEventManager();
-            hyperlinkTooltipView = new gui.HyperlinkTooltipView(session.getOdtDocument().getOdfCanvas(),
+            hyperlinkTooltipView = new gui.HyperlinkTooltipView(odfCanvas,
                                                     self.sessionController.getHyperlinkClickHandler().getModifier);
             eventManager.subscribe("mousemove", hyperlinkTooltipView.showTooltip);
             eventManager.subscribe("mouseout", hyperlinkTooltipView.hideTooltip);
 
-            caretManager = new gui.CaretManager(self.sessionController);
+            caretManager = new gui.CaretManager(self.sessionController, odfCanvas.getViewport());
             selectionViewManager = new gui.SelectionViewManager(gui.SvgSelectionView);
             self.sessionView = new gui.SessionView(config.viewOptions, localMemberId, session, sessionConstraints, caretManager, selectionViewManager);
             self.availableFonts = getAvailableFonts();

+ 1 - 2
main/inc/lib/javascript/wodotexteditor/FullWindowZoomHelper.js

@@ -1,5 +1,4 @@
 /**
- * @license
  * Copyright (C) 2014 KO GmbH <copyright@kogmbh.com>
  *
  * @licstart
@@ -42,7 +41,7 @@ define("webodf/editor/FullWindowZoomHelper", [], function () {
             canvasContainerElement.style.top = toolbarContainerElement.getBoundingClientRect().height + 'px';
         }
 
-        this.destroy = function(callback) {
+        this.destroy = function (callback) {
             window.removeEventListener('scroll', translateToolbar);
             window.removeEventListener('focusout', translateToolbar);
             window.removeEventListener('touchmove', translateToolbar);

+ 149 - 0
main/inc/lib/javascript/wodotexteditor/HOWTO.md

@@ -0,0 +1,149 @@
+# HOWTO use the Wodo.TextEditor component
+
+## Requirements
+
+The Wodo.TextEditor component needs to be included in webpages served by a webserver, as its code uses XHR requests to fetch more data/files where needed.
+
+## Creating the Editor
+
+The file which needs to be included to create a Wodo.TextEditor is `wodotexteditor.js`.
+
+    <head>
+    <!-- ... -->
+
+    <script src="wodotexteditor/wodotexteditor.js" type="text/javascript" charset="utf-8"></script>
+
+    <!-- ... -->
+    </head>
+
+In the body of the HTML there needs to be set a div element which should hold the editor, with a unique id value and a size as wanted.
+
+    <body>
+    <!-- ... -->
+
+    <div id="editorContainer" style="width: 600px;height: 600px;"></div>
+
+    <!-- ... -->
+    </body>
+
+An instance of the editor is created in JavaScript by calling `Wodo.createTextEditor()`:
+
+    <script type="text/javascript">
+    // ...
+
+    Wodo.createTextEditor('editorContainer', {
+        allFeaturesEnabled: true,
+        userData: {
+            fullName: "Tim Lee",
+            color:    "blue"
+        }
+    }, function (err, editor) {
+        if (err) {
+            // something failed unexpectedly, deal with it (here just a simple alert)
+            alert(err);
+            return;
+        }
+        editor.openDocumentFromUrl("document.odt", function(err) {
+            if (err) {
+                // something failed unexpectedly, deal with it (here just a simple alert)
+                alert("There was an error on opening the document: " + err);
+            }
+        });
+    });
+
+    // ...
+    </script>
+
+See the example file [`texteditor.html`](texteditor.html) how the above can be applied.
+
+Once the editing should be done, the current state of the document can be retrieved from the editor by calling `editor.getDocumentAsByteArray()`:
+
+    editor.getDocumentAsByteArray(function (err, odtFileAsByteArray) {
+        if (err) {
+            // something failed unexpectedly, deal with it (here just a simple alert)
+            alert(err);
+            return;
+        }
+        // now do something with odtFileAsByteArray (is of type Uint8Array)
+    });
+
+See the example file [`localfileeditor.js`](localfileeditor.js) how the above can be applied.
+
+To track if the user has edited the document after it was loaded or since it has been synchronized the last time, the editor has a property "documentModified".
+The current state of the document can be set as unmodified by calling
+
+    editor.setDocumentModified(false);
+
+e.g. when the document has been synchronized somewhere. To query if the document is modified call `isDocumentModified()` on the editor object,
+
+    if (editor.isDocumentModified()) {
+        // ask the user if the changes should be discarded
+    }
+
+See the example file [`localfileeditor.js`](localfileeditor.js) how the above can be applied.
+
+When calling `Wodo.createTextEditor()`, the second parameter allows to configure the editor. See the included API dox for the possible parameters.
+
+
+### Examples
+
+For inspiration there are three examples included how to use the Wodo.TextEditor component.
+
+#### Simple Editor
+
+The file [`texteditor.html`](texteditor.html) holds a very simple example of creating an editor and loading a given file into it, so that it can be edited.
+It does not deal with getting the result and storing it somewhere.
+
+#### Editor which lets user edit files from local filesystem
+
+The file [`localeditor.html`](localeditor.html) extends the simple editor example above by adding two things:
+support for loading files from the local filesystem into the editor and support for storing the current state of the edited document using the "saveAs" feature of browsers.
+
+The example consists of the files [`localfileeditor.js`](localfileeditor.js) and [`localeditor.html`](localeditor.html). Additionally it uses [`FileSaver.js`](FileSaver.js), which is a polyfill for browsers not yet supporting "saveAs".
+
+#### Editor which lets user review files from local filesystem
+
+The file [`revieweditor.html`](revieweditor.html) is a variant of the editor for files of the local filesystem:
+it sets the editing modus to "review", which limits the user to only add, edit and remove own annotations, but not change anything else in the document.
+
+The example consists of the files [`revieweditor.js`](revieweditor.js) and [`revieweditor.html`](revieweditor.html). Additionally it uses [`FileSaver.js`](FileSaver.js), which is a polyfill for browsers not yet supporting "saveAs".
+
+
+## External Resources
+
+ODF documents can reference external resources, like images or fonts, insteading of embedding them. This is done to keep the size of the files smaller.
+It makes a lot of sense for resources which are shared by many documents, like usually the fonts used. Of course this requires that those resources are
+available on the system where the document is rendered, e.g. by serving them on request from a central server.
+
+The Wodo.TextEditor currently supports the central serving of fonts.
+
+### Fonts
+
+Font files on the server are registered to the Wodo.TextEditor by listing them in a CSS file. Each font is registered with a normal `@font-face` CSS rule.
+The file is name `fonts.css` and placed in the subdirectory `fonts` of the `resources` directory.
+This file will be automatically read by the Wodo.TextEditor. The font files can be put whereever it suits, as long as their location is correctly given in `fonts.css`.
+
+It surely makes sense to place the fonts into the same directory like "fonts.css", unless also used for other purposes.
+(TODO: instead of a shared "resources" path perhaps there should be an entry just for fonts/fonts.css)
+
+
+Example:
+
+The Wodo.TextEditor installation is in the path `/wodotexteditor` of the webserver. This requires that the fonts files are listed in
+`/wodotexteditor/resources/fonts/fonts.css`.
+There is a font with the font-family name "Gentium Basic" in file name `GenBasR.ttf`. The file is placed in `/wodotexteditor/resources/fonts`.
+So it will be listed in `fonts.css` as
+
+    @font-face {
+        font-family: "Gentium Basic";
+        src: url("./GenBasR.ttf") format("truetype");
+        font-weight: normal;
+        font-style: normal;
+    }
+
+
+## F.A.Q
+
+1.  Why is a document which is hosted on another server not loading?
+
+ Web pages with the Wodo.TextEditor must be served with the flag to allow its code to access code from servers in other domains. See https://en.wikipedia.org/wiki/Cross-origin_resource_sharing

+ 39 - 0
main/inc/lib/javascript/wodotexteditor/README.md

@@ -0,0 +1,39 @@
+## Wodo.TextEditor
+
+Wodo.TextEditor is an HTML5 Editor component for text documents in the OpenDocument format (ODT) created by [KO GmbH](http://kogmbh.com).
+
+It allows to add an OpenDocument Text editor with just a few lines of JavaScript to your website and to your mobile or desktop application.
+It uses only HTML, CSS, and JavaScript and has no need for any server support. Powered by WebODF.
+
+* Visit the project homepage at: [WebODF](http://webodf.org)
+* Want some live demos? Visit: [Wodo.TextEditor Demo](http://webodf.org/demo/)
+* Get in contact, by the [mailing list](https://lists.opendocsociety.org/mailman/listinfo/webodf) or IRC (#webodf auf freenode, [Web access](http://webchat.freenode.net/?nick=wodotexteditorcurious_ghr&channels=webodf))
+
+### License
+
+Wodo.TextEditor is a Free Software project. All code is available under the AGPL.
+
+If you are interested in using Wodo.TextEditor in your commercial product
+(and do not want to disclose your sources / obey AGPL),
+contact [KO GmbH](http://kogmbh.com) for a license suited to your needs.
+
+
+### Supported Dependency/Package Managers
+
+This repository is explicitely done for dependency managers and package managers.
+It only contains the files of builds created from released versions of Wodo.TextEditor, so
+they can be directly deployed by those managers.
+
+Currently supported by this repository:
+* [Bower](http://bower.io/), registered in the central Bower registry by: "wodo.texteditor"
+
+We are looking forward to support other managers as well, help welcome.
+
+To get tarballs of the released builds, go to http://webodf.org/download .
+
+For the sources see section [Sources](#Sources).
+
+
+### Sources
+
+Find the sources of Wodo.TextEditor at https://github.com/kogmbh/WebODF/ .

+ 97 - 50
main/inc/lib/javascript/wodotexteditor/Tools.js

@@ -22,7 +22,7 @@
  * @source: https://github.com/kogmbh/WebODF/
  */
 
-/*global define,document,require,ops */
+/*global window, define, require, document, dijit, dojo, runtime, ops*/
 
 define("webodf/editor/Tools", [
     "dojo/ready",
@@ -50,39 +50,44 @@ define("webodf/editor/Tools", [
                 onToolDone = args.onToolDone,
                 loadOdtFile = args.loadOdtFile,
                 saveOdtFile = args.saveOdtFile,
+                saveAsOdtFile = args.saveAsOdtFile,
+                downloadOdtFile = args.downloadOdtFile,
                 close = args.close,
                 toolbar,
                 loadButton, saveButton, closeButton, aboutButton,
+                saveAsButton, downloadButton,
                 formatDropDownMenu, formatMenuButton,
-                paragraphStylesMenuItem, paragraphStylesDialog, simpleStyles, currentStyle,
-                zoomSlider,
-                undoRedoMenu,
+                paragraphStylesMenuItem, paragraphStylesDialog,
                 editorSession,
-                paragraphAlignment,
-                imageInserter,
-                annotationControl,
-                editHyperlinks,
                 aboutDialog,
                 sessionSubscribers = [];
 
+            function placeAndStartUpWidget(widget) {
+                widget.placeAt(toolbar);
+                widget.startup();
+            }
+
             /**
              * Creates a tool and installs it, if the enabled flag is set to true.
              * Only supports tool classes whose constructor has a single argument which
              * is a callback to pass the created widget object to.
              * @param {!function(new:Object, function(!Object):undefined)} Tool  constructor method of the tool
              * @param {!boolean} enabled
+             * @param {!Object|undefined=} config
              * @return {?Object}
              */
-            function createTool(Tool, enabled) {
+            function createTool(Tool, enabled, config) {
                 var tool = null;
 
                 if (enabled) {
-                    tool = new Tool(function (widget) {
-                        widget.placeAt(toolbar);
-                        widget.startup();
-                    });
+                    if (config) {
+                        tool = new Tool(config, placeAndStartUpWidget);
+                    } else {
+                        tool = new Tool(placeAndStartUpWidget);
+                    }
                     sessionSubscribers.push(tool);
                     tool.onToolDone = onToolDone;
+                    tool.setEditorSession(editorSession);
                 }
 
                 return tool;
@@ -108,9 +113,12 @@ define("webodf/editor/Tools", [
                 sessionSubscribers.forEach(function (subscriber) {
                     subscriber.setEditorSession(editorSession);
                 });
-                if (formatMenuButton) {
-                    formatMenuButton.setAttribute('disabled', !editorSession);
-                }
+
+                [saveButton, saveAsButton, downloadButton, closeButton, formatMenuButton].forEach(function (button) {
+                    if (button) {
+                        button.setAttribute('disabled', !editorSession);
+                    }
+                });
             }
 
             this.setEditorSession = setEditorSession;
@@ -144,10 +152,7 @@ define("webodf/editor/Tools", [
                     aboutButton = new Button({
                         label: tr('About WebODF Text Editor'),
                         showLabel: false,
-                        iconClass: 'webodfeditor-dijitWebODFIcon',
-                        style: {
-                            float: 'left'
-                        }
+                        iconClass: 'webodfeditor-dijitWebODFIcon'
                     });
                     aboutDialog = new AboutDialog(function (dialog) {
                         aboutButton.onClick = function () {
@@ -159,33 +164,12 @@ define("webodf/editor/Tools", [
                     aboutButton.placeAt(toolbar);
                 }
 
-                // Undo/Redo
-                undoRedoMenu = createTool(UndoRedoMenu, args.undoRedoEnabled);
-
-                // Add annotation
-                annotationControl = createTool(AnnotationControl, args.annotationsEnabled);
-
-                // Simple Style Selector [B, I, U, S]
-                simpleStyles = createTool(SimpleStyles, args.directTextStylingEnabled);
-
-                // Paragraph direct alignment buttons
-                paragraphAlignment = createTool(ParagraphAlignment, args.directParagraphStylingEnabled);
-
-                // Paragraph Style Selector
-                currentStyle = createTool(CurrentStyle, args.paragraphStyleSelectingEnabled);
-
-                // Zoom Level Selector
-                zoomSlider = createTool(ZoomSlider, args.zoomingEnabled);
-
                 // Load
                 if (loadOdtFile) {
                     loadButton = new Button({
                         label: tr('Open'),
                         showLabel: false,
                         iconClass: 'dijitIcon dijitIconFolderOpen',
-                        style: {
-                            float: 'left'
-                        },
                         onClick: function () {
                             loadOdtFile();
                         }
@@ -198,10 +182,8 @@ define("webodf/editor/Tools", [
                     saveButton = new Button({
                         label: tr('Save'),
                         showLabel: false,
+                        disabled: true,
                         iconClass: 'dijitEditorIcon dijitEditorIconSave',
-                        style: {
-                            float: 'left'
-                        },
                         onClick: function () {
                             saveOdtFile();
                             onToolDone();
@@ -210,6 +192,38 @@ define("webodf/editor/Tools", [
                     saveButton.placeAt(toolbar);
                 }
 
+                // SaveAs
+                if (saveAsOdtFile) {
+                    saveAsButton = new Button({
+                        label: tr('Save as...'),
+                        showLabel: false,
+                        disabled: true,
+                        iconClass: 'webodfeditor-dijitSaveAsIcon',
+                        onClick: function () {
+                            saveAsOdtFile();
+                            onToolDone();
+                        }
+                    });
+                    saveAsButton.placeAt(toolbar);
+                }
+
+                // Download
+                if (downloadOdtFile) {
+                    downloadButton = new Button({
+                        label: tr('Download'),
+                        showLabel: true,
+                        disabled: true,
+                        style: {
+                            float: 'right'
+                        },
+                        onClick: function () {
+                            downloadOdtFile();
+                            onToolDone();
+                        }
+                    });
+                    downloadButton.placeAt(toolbar);
+                }
+
                 // Format menu
                 if (args.paragraphStyleEditingEnabled) {
                     formatDropDownMenu = new DropDownMenu({});
@@ -233,25 +247,41 @@ define("webodf/editor/Tools", [
                         dropDown: formatDropDownMenu,
                         disabled: true,
                         label: tr('Format'),
-                        iconClass: "dijitIconEditTask",
-                        style: {
-                            float: 'left'
-                        }
+                        iconClass: "dijitIconEditTask"
                     });
                     formatMenuButton.placeAt(toolbar);
                 }
 
+                // Undo/Redo
+                createTool(UndoRedoMenu, args.undoRedoEnabled);
+
+                // Add annotation
+                createTool(AnnotationControl, args.annotationsEnabled);
+
+                // Simple Style Selector [B, I, U, S]
+                createTool(SimpleStyles, args.directTextStylingEnabled);
+
+                // Paragraph direct alignment buttons
+                createTool(ParagraphAlignment, args.directParagraphStylingEnabled);
+
+                // Paragraph Style Selector
+                createTool(CurrentStyle, args.paragraphStyleSelectingEnabled);
+
+                // Zoom Level Selector
+                createTool(ZoomSlider, args.zoomingEnabled);
+
                 // hyper links
-                editHyperlinks = createTool(EditHyperlinks, args.hyperlinkEditingEnabled);
+                createTool(EditHyperlinks, args.hyperlinkEditingEnabled);
 
                 // image insertion
-                imageInserter = createTool(ImageInserter, args.imageInsertingEnabled);
+                createTool(ImageInserter, args.imageInsertingEnabled);
 
                 // close button
                 if (close) {
                     closeButton = new Button({
                         label: tr('Close'),
                         showLabel: false,
+                        disabled: true,
                         iconClass: 'dijitEditorIcon dijitEditorIconCancel',
                         style: {
                             float: 'right'
@@ -263,6 +293,23 @@ define("webodf/editor/Tools", [
                     closeButton.placeAt(toolbar);
                 }
 
+                // This is an internal hook for debugging/testing.
+                // Yes, you discovered something interesting. But:
+                // Do NOT rely on it, it will not be supported and can and will change in any version.
+                // It is not officially documented for a reason. A real plugin system is only on the wishlist
+                // so far, please file your suggestions/needs at the official WebODF issue system.
+                // You have been warned.
+                if (window.wodo_plugins) {
+                    window.wodo_plugins.forEach(function (plugin) {
+                        runtime.log("Creating plugin: "+plugin.id);
+                        require([plugin.id], function (Plugin) {
+                            runtime.log("Creating as tool now: "+plugin.id);
+                            createTool(Plugin, true, plugin.config);
+                        });
+                    });
+
+                }
+
                 setEditorSession(editorSession);
             });
         };

+ 5 - 1
main/inc/lib/javascript/wodotexteditor/Translator.js

@@ -22,7 +22,7 @@
  * @source: https://github.com/kogmbh/WebODF/
  */
 
-/*global define, runtime, XMLHTTPRequest */
+/*global define, runtime, XMLHttpRequest */
 
 define("webodf/editor/Translator", [], function () {
     "use strict";
@@ -40,8 +40,12 @@ define("webodf/editor/Translator", [], function () {
                 newLocale = "de-DE";
             } else if (newLocale.split('-')[0] === "nl" || newLocale.split('_')[0] === "nl") {
                 newLocale = "nl-NL";
+            } else if (newLocale.split('-')[0] === "fr" || newLocale.split('_')[0] === "fr") {
+                newLocale = "fr-FR";
             } else if (newLocale.split('-')[0] === "it" || newLocale.split('_')[0] === "it") {
                 newLocale = "it-IT";
+            } else if (newLocale.split('-')[0] === "eu" || newLocale.split('_')[0] === "eu") {
+                newLocale = "eu";
             } else if (newLocale.split('-')[0] === "en" || newLocale.split('_')[0] === "en") {
                 newLocale = "en-US";
             } else {

Dosya farkı çok büyük olduğundan ihmal edildi
+ 511 - 501
main/inc/lib/javascript/wodotexteditor/dojo-amalgamation.js


BIN
main/inc/lib/javascript/wodotexteditor/images/saveas.png


+ 33 - 21
main/inc/lib/javascript/wodotexteditor/localfileeditor.js

@@ -22,6 +22,8 @@
  * @source: https://github.com/kogmbh/WebODF/
  */
 
+/*global document, window, runtime, FileReader, alert, Uint8Array, Blob, saveAs, Wodo*/
+
 function createEditor(path) {
     "use strict";
 
@@ -29,11 +31,13 @@ function createEditor(path) {
         editorOptions,
         loadedFilename;
 
+    /*jslint emptyblock: true*/
     /**
      * @return {undefined}
      */
-     function startEditing() {
-     }
+    function startEditing() {
+    }
+    /*jslint emptyblock: false*/
 
     /**
      * extract document url from the url-fragment
@@ -66,12 +70,15 @@ function createEditor(path) {
             }
         }
         if (files && files.length === 1) {
-            editor.closeDocument(function() {
-                file = files[0];
-                reader = new FileReader();
-                reader.onloadend = onLoadEnd;
-                reader.readAsArrayBuffer(file);
-            });
+            if (!editor.isDocumentModified() ||
+                window.confirm("There are unsaved changes to the file. Do you want to discard them?")) {
+                editor.closeDocument(function() {
+                    file = files[0];
+                    reader = new FileReader();
+                    reader.onloadend = onLoadEnd;
+                    reader.readAsArrayBuffer(file);
+                });
+            }
         } else {
             alert("File could not be opened in this browser.");
         }
@@ -79,22 +86,14 @@ function createEditor(path) {
 
     function enhanceRuntime() {
         var openedFiles = {},
-            read = runtime.read,
-            getFileSize = runtime.getFileSize;
-        runtime.read = function (path, offset, length, callback) {
+            readFile = runtime.readFile;
+        runtime.readFile = function (path, encoding, callback) {
             var array;
             if (openedFiles.hasOwnProperty(path)) {
-                array = new Uint8Array(openedFiles[path], offset, length);
+                array = new Uint8Array(openedFiles[path]);
                 callback(undefined, array);
             } else {
-                return read(path, offset, length, callback);
-            }
-        };
-        runtime.getFileSize = function (path, callback) {
-            if (openedFiles.hasOwnProperty(path)) {
-                return callback(openedFiles[path].byteLength);
-            } else {
-                return getFileSize(path, callback);
+                return readFile(path, encoding, callback);
             }
         };
         runtime.registerFile = function (path, data) {
@@ -142,6 +141,8 @@ function createEditor(path) {
                 filename = loadedFilename || "doc.odt",
                 blob = new Blob([data.buffer], {type: mimetype});
             saveAs(blob, filename);
+            // TODO: hm, saveAs could fail or be cancelled
+            editor.setDocumentModified(false);
         }
 
         editor.getDocumentAsByteArray(saveByteArrayLocally);
@@ -155,7 +156,7 @@ function createEditor(path) {
 
     function onEditorCreated(err, e) {
         var docUrl = guessDocUrl();
-        console.log(docUrl);
+
         if (err) {
             // something failed unexpectedly
             alert(err);
@@ -168,6 +169,17 @@ function createEditor(path) {
             color:    "black"
         });
 
+        window.addEventListener("beforeunload", function (e) {
+            var confirmationMessage = "There are unsaved changes to the file.";
+
+            if (editor.isDocumentModified()) {
+                // Gecko + IE
+                (e || window.event).returnValue = confirmationMessage;
+                // Webkit, Safari, Chrome etc.
+                return confirmationMessage;
+            }
+        });
+
         if (docUrl) {
             loadedFilename = docUrl;
             editor.openDocumentFromUrl(docUrl, startEditing);

+ 189 - 0
main/inc/lib/javascript/wodotexteditor/revieweditor.js

@@ -0,0 +1,189 @@
+/**
+ * Copyright (C) 2014 KO GmbH <copyright@kogmbh.com>
+ *
+ * @licstart
+ * This file is part of WebODF.
+ *
+ * WebODF is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License (GNU AGPL)
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * WebODF 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 WebODF.  If not, see <http://www.gnu.org/licenses/>.
+ * @licend
+ *
+ * @source: http://www.webodf.org/
+ * @source: https://github.com/kogmbh/WebODF/
+ */
+
+/*global document, window, runtime, FileReader, alert, Uint8Array, Blob, saveAs, Wodo*/
+
+function createReviewEditor() {
+    "use strict";
+
+    var editor = null,
+        editorOptions,
+        loadedFilename;
+
+    /*jslint emptyblock: true*/
+    /**
+     * @return {undefined}
+     */
+    function startEditing() {
+    }
+    /*jslint emptyblock: false*/
+
+    /**
+     * extract document url from the url-fragment
+     *
+     * @return {?string}
+     */
+    function guessDocUrl() {
+        var pos, docUrl = String(document.location);
+        // If the URL has a fragment (#...), try to load the file it represents
+        pos = docUrl.indexOf('#');
+        if (pos !== -1) {
+            docUrl = docUrl.substr(pos + 1);
+        } else {
+            docUrl = "welcome.odt";
+        }
+        return docUrl || null;
+    }
+
+    function fileSelectHandler(evt) {
+        var file, files, reader;
+        files = (evt.target && evt.target.files) ||
+            (evt.dataTransfer && evt.dataTransfer.files);
+        function onLoadEnd() {
+            if (reader.readyState === 2) {
+                runtime.registerFile(file.name, reader.result);
+                loadedFilename = file.name;
+                editor.openDocumentFromUrl(loadedFilename, startEditing);
+            }
+        }
+        if (files && files.length === 1) {
+            if (!editor.isDocumentModified() ||
+                window.confirm("There are unsaved changes to the file. Do you want to discard them?")) {
+                editor.closeDocument(function() {
+                    file = files[0];
+                    reader = new FileReader();
+                    reader.onloadend = onLoadEnd;
+                    reader.readAsArrayBuffer(file);
+                });
+            }
+        } else {
+            alert("File could not be opened in this browser.");
+        }
+    }
+
+    function enhanceRuntime() {
+        var openedFiles = {},
+            readFile = runtime.readFile;
+        runtime.readFile = function (path, encoding, callback) {
+            var array;
+            if (openedFiles.hasOwnProperty(path)) {
+                array = new Uint8Array(openedFiles[path]);
+                callback(undefined, array);
+            } else {
+                return readFile(path, encoding, callback);
+            }
+        };
+        runtime.registerFile = function (path, data) {
+            openedFiles[path] = data;
+        };
+    }
+
+    function createFileLoadForm() {
+        var form = document.createElement("form"),
+            input = document.createElement("input");
+
+        function internalHandler(evt) {
+            if (input.value !== "") {
+                fileSelectHandler(evt);
+            }
+            // reset to "", so selecting the same file next time still trigger the change handler
+            input.value = "";
+        }
+        form.appendChild(input);
+        form.style.display = "none";
+        input.id = "fileloader";
+        input.setAttribute("type", "file");
+        input.addEventListener("change", internalHandler, false);
+        document.body.appendChild(form);
+    }
+
+    function load() {
+        var form = document.getElementById("fileloader");
+        if (!form) {
+            enhanceRuntime();
+            createFileLoadForm();
+            form = document.getElementById("fileloader");
+        }
+        form.click();
+    }
+
+    function save() {
+        function saveByteArrayLocally(err, data) {
+            if (err) {
+                alert(err);
+                return;
+            }
+            // TODO: odfcontainer should have a property mimetype
+            var mimetype = "application/vnd.oasis.opendocument.text",
+                filename = loadedFilename || "doc.odt",
+                blob = new Blob([data.buffer], {type: mimetype});
+            saveAs(blob, filename);
+            // TODO: hm, saveAs could fail or be cancelled
+            editor.setDocumentModified(false);
+        }
+
+        editor.getDocumentAsByteArray(saveByteArrayLocally);
+    }
+
+    editorOptions = {
+        loadCallback: load,
+        saveCallback: save,
+        modus: Wodo.MODUS_REVIEW,
+        allFeaturesEnabled: true
+    };
+
+    function onEditorCreated(err, e) {
+        var docUrl = guessDocUrl();
+
+        if (err) {
+            // something failed unexpectedly
+            alert(err);
+            return;
+        }
+
+        editor = e;
+        editor.setUserData({
+            fullName: "Curious WebODF-Reviewer",
+            color:    "black"
+        });
+
+        window.addEventListener("beforeunload", function (e) {
+            var confirmationMessage = "There are unsaved changes to the file.";
+
+            if (editor.isDocumentModified()) {
+                // Gecko + IE
+                (e || window.event).returnValue = confirmationMessage;
+                // Webkit, Safari, Chrome etc.
+                return confirmationMessage;
+            }
+        });
+
+        if (docUrl) {
+            loadedFilename = docUrl;
+            editor.openDocumentFromUrl(docUrl, startEditing);
+        }
+    }
+
+    Wodo.createTextEditor('editorContainer', editorOptions, onEditorCreated);
+}

+ 2 - 2
main/inc/lib/javascript/wodotexteditor/translations/de-DE.json

@@ -5,6 +5,7 @@
     "File": "Datei",
     "Open": "Öffnen",
     "Save": "Speichern",
+    "Save as...": "Speichern unter...",
     "Edit": "Bearbeiten",
     "View": "Ansicht",
     "Annotate": "Kommentieren",
@@ -19,6 +20,7 @@
     "Delete": "Entferne",
     "Insert": "Einfügen",
     "Format": "Formatieren",
+    "Download": "Download",
     "Close": "Schließe",
     "Character...": "Zeichen...",
     "Paragraph...": "Absatz...",
@@ -26,8 +28,6 @@
     "Cancel": "Abbrechen",
     "Alignment": "Ausrichtung",
     "Font Effects": "Schrifteffekte",
-    "Outline and Numbering": "Gliederung & Aufzählung",
-    "Text Flow": "Textfluß",
     "Character": "Zeichen",
     "Paragraph Styles": "Absatzstile",
     "Clone this Style": "Kopiere diesen Stil",

+ 64 - 0
main/inc/lib/javascript/wodotexteditor/translations/eu.json

@@ -0,0 +1,64 @@
+{
+    "Loading": "Kargatzen",
+    "Unknown Author": "Egile ezezaguna",
+
+    "File": "Fitxategia",
+    "Open": "Ireki",
+    "Save": "Gorde",
+    "Edit": "Editatu",
+    "View": "Ikusi",
+    "Annotate": "Egin oharpenak",
+    "Align Left": "Lerrokatu ezkerrean",
+    "Center": "Lerrokatu erdian",
+    "Align Right": "Lerrokatu eskuinean",
+    "Justify" : "Justifikatu",
+    "Increase Indent": "Handitu koska",
+    "Decrease Indent": "Txikitu koska",
+    "Clone": "Klonatu",
+    "Create": "Sortu",
+    "Delete": "Ezabatu",
+    "Insert": "Txertatu",
+    "Format": "Formatua",
+    "Close": "Itxi",
+    "Character...": "Karakterea...",
+    "Paragraph...": "Paragrafoa...",
+    "OK": "Ados",
+    "Cancel": "Utzi",
+    "Alignment": "Lerrokatzea",
+    "Font Effects": "Letra-tipoaren efektuak",
+    "Character": "Karakterea",
+    "Paragraph Styles": "Paragrafo-estiloak",
+    "Clone this Style": "Klonatu estilo hau",
+    "New Name:": "Izen berria:",
+    "Collaboration Pane": "Lankidetza-panela",
+    "Members": "Kideak",
+    "Invite Members": "Gonbidatu kideak",
+    "Left": "Ezkerra",
+    "Right": "Eskuina",
+    "Top": "Goia",
+    "Bottom": "Behea",
+    "Justified": "Justifikatua",
+    "Spacing": "Tarteak",
+    "Options": "Aukerak",
+    "Style": "Estiloa",
+    "Undo": "Desegin",
+    "Redo": "Berregin",
+    "Bold": "Lodia",
+    "Italic": "Etzana",
+    "Underline": "Azpimarratua",
+    "Strikethrough": "Marratua",
+    "Font": "Letra-tipoa",
+    "Family": "Familia",
+    "Size": "Tamaina",
+    "Color": "Kolorea",
+    "Text": "Testua",
+    "Background": "Atzeko planoa",
+    "Default Style": "Estilo lehenetsia",
+    "Insert Image": "Txertatu irudia",
+    "Edit link": "Editatu esteka",
+    "Remove link": "Kendu esteka",
+    "Display Text": "Bistaratu testua",
+    "URL": "URL",
+    "Ctrl-click to follow link": "Ktrl-klik esteka jarraitzeko",
+    "⌘-click to follow link": "⌘-klik esteka jarraitzeko"
+}

+ 66 - 0
main/inc/lib/javascript/wodotexteditor/translations/fr-FR.json

@@ -0,0 +1,66 @@
+{
+    "Loading": "Chargement de",
+    "Unknown Author": "Auteur inconnu",
+
+    "File": "Fichier",
+    "Open": "Ouvrir",
+    "Save": "Enregistrer",
+    "Save as...": "Enregistrer sous…",
+    "Edit": "Éditer",
+    "View": "Vue",
+    "Annotate": "Annoter",
+    "Align Left": "Aligné à gauche",
+    "Center": "Centré",
+    "Align Right": "Aligné à droite",
+    "Justify" : "Justifier",
+    "Increase Indent": "Augmenter l'indentation",
+    "Decrease Indent": "Diminuer l'indentation",
+    "Clone": "Copier",
+    "Create": "Créer",
+    "Delete": "Supprimer",
+    "Insert": "Insérer",
+    "Format": "Formater",
+    "Download": "Téléchargement",
+    "Close": "Fermer",
+    "Character...": "Caractère…",
+    "Paragraph...": "Paragraphe…",
+    "OK": "OK",
+    "Cancel": "Annuler",
+    "Alignment": "Alignement",
+    "Font Effects": "Effets de caractère",
+    "Character": "Caractère",
+    "Paragraph Styles": "Styles de Paragraphe",
+    "Clone this Style": "Copier ce Style",
+    "New Name:": "Nouveau Nom :",
+    "Collaboration Pane": "Collaboration",
+    "Members": "Participants",
+    "Invite Members": "Inviter des participants",
+    "Left": "Gauche",
+    "Right": "Droite",
+    "Top": "Haut",
+    "Bottom": "Bas",
+    "Justified": "Justification",
+    "Spacing": "Espacement",
+    "Options": "Options",
+    "Style": "Style",
+    "Undo": "Défaire",
+    "Redo": "Refaire",
+    "Bold": "Gras",
+    "Italic": "Italique",
+    "Underline": "Souligné",
+    "Strikethrough": "Barré",
+    "Font": "Police",
+    "Family": "Famille",
+    "Size": "Taille",
+    "Color": "Couleur",
+    "Text": "Texte",
+    "Background": "Arrière-plan",
+    "Default Style": "Style par défaut",
+    "Insert Image": "Insérer une image",
+    "Edit link": "Éditer le lien",
+    "Remove link": "Supprimer le lien",
+    "Display Text": "Texte du lien",
+    "URL": "URL",
+    "Ctrl-click to follow link": "Ctrl-clic pour ouvrir le lien",
+    "⌘-click to follow link": "⌘-clic pour ouvrir le lien"
+}

+ 0 - 2
main/inc/lib/javascript/wodotexteditor/translations/it-IT.json

@@ -26,8 +26,6 @@
     "Cancel": "Annulla",
     "Alignment": "Allineamento",
     "Font Effects": "Effetti del carattere",
-    "Outline and Numbering": "Contorno e numerazione",
-    "Text Flow": "Flusso del testo",
     "Character": "Carattere",
     "Paragraph Styles": "Stile paragrafo",
     "Clone this Style": "Copia questo stile",

+ 2 - 2
main/inc/lib/javascript/wodotexteditor/translations/nl-NL.json

@@ -5,6 +5,7 @@
     "File": "Bestand",
     "Open": "Openen",
     "Save": "Opslaan",
+    "Save as...": "Opslaan als...",
     "Edit": "Bewerken",
     "View": "Beeld",
     "Annotate": "Notitie toevoegen",
@@ -19,6 +20,7 @@
     "Delete": "Verwijderen",
     "Insert": "Invoegen",
     "Format": "Opmaak",
+    "Download": "Download",
     "Close": "Sluiten",
     "Character...": "Teken...",
     "Paragraph...": "Alinea...",
@@ -26,8 +28,6 @@
     "Cancel": "Annuleren",
     "Alignment": "Uitlijning",
     "Font Effects": "Teksteffecten",
-    "Outline and Numbering": "Nummering/opsommingstekens",
-    "Text Flow": "Tekstverloop",
     "Character": "Teken",
     "Paragraph Styles": "Alineaopmaak",
     "Clone this Style": "Kopieer deze opmaak",

Dosya farkı çok büyük olduğundan ihmal edildi
+ 615 - 594
main/inc/lib/javascript/wodotexteditor/webodf.js


+ 11 - 9
main/inc/lib/javascript/wodotexteditor/widgets/aboutDialog.js

@@ -22,39 +22,41 @@
  * @source: https://github.com/kogmbh/WebODF/
  */
 
-/*global define,require,webodf */
+/*global define, dojo, runtime, webodf */
 
 define("webodf/editor/widgets/aboutDialog", ["dijit/Dialog"], function (Dialog) {
     "use strict";
 
     var editorBase = dojo.config && dojo.config.paths && dojo.config.paths["webodf/editor"],
-        kogmbhImageUrl = editorBase+ "/images/kogmbh.png";
+        kogmbhImageUrl = editorBase + "/images/kogmbh.png";
 
     runtime.assert(editorBase, "webodf/editor path not defined in dojoConfig");
 
     return function AboutDialog(callback) {
         var self = this;
 
+        /*jslint emptyblock: true*/
         this.onToolDone = function () {};
+        /*jslint emptyblock: false*/
 
         function init() {
             // TODO: translation, once the the about text has been decided about
-            var tr = runtime.tr,
-                dialog;
+            var dialog;
 
             // Dialog
             dialog = new Dialog({
                 style: "width: 400px",
-                title: "WebODF Text Editor",
+                title: "Wodo.TextEditor",
                 autofocus: false,
-                content: "<p>The WebODF Text Editor is an easy to use Javascript-based plugin for webpages. " +
-                            "It provides a stand-alone editor for text documents in the OpenDocument Format.</p>" +
+                content: "<p>Wodo.TextEditor is an easy to use Javascript-based plugin for webpages. " +
+                            "It provides a stand-alone editor for text documents in the OpenDocument Format. " +
+                            "Done with WebODF.</p>" +
                             //TODO: add proper link directly to page about the component
                             "<p>Learn more on the <a href=\"http://webodf.org/\" target=\"_blank\">WebODF website</a>.</p>" +
                             "<p>Version " + webodf.Version + "</p>" +
-                            "<p>Made by <a href=\"http://kogmbh.com\" target=\"_blank\"><img src=\""+kogmbhImageUrl+"\" width=\"172\" height=\"40\" alt=\"KO GmbH\" style=\"vertical-align: top;\"></a>.</p>"
+                            "<p>Made by <a href=\"http://kogmbh.com\" target=\"_blank\"><img src=\"" + kogmbhImageUrl + "\" width=\"172\" height=\"40\" alt=\"KO GmbH\" style=\"vertical-align: top;\"></a>.</p>"
             });
-            dialog.onHide = function() { self.onToolDone(); };
+            dialog.onHide = function () { self.onToolDone(); };
 
             callback(dialog);
         }

+ 4 - 3
main/inc/lib/javascript/wodotexteditor/widgets/annotation.js

@@ -22,7 +22,7 @@
  * @source: https://github.com/kogmbh/WebODF/
  */
 
-/*global define, require */
+/*global define, require, runtime, gui */
 
 define("webodf/editor/widgets/annotation", [
     "dijit/form/Button"],
@@ -84,11 +84,12 @@ define("webodf/editor/widgets/annotation", [
                 }
             };
 
+            /*jslint emptyblock: true*/
             this.onToolDone = function () {};
+            /*jslint emptyblock: false*/
 
             callback(widget);
         };
 
         return AnnotationControl;
-    }
-);
+    });

+ 19 - 16
main/inc/lib/javascript/wodotexteditor/widgets/dialogWidgets/alignmentPane.js

@@ -24,13 +24,16 @@
 
 /*global runtime,core,define,require,dijit */
 
-define("webodf/editor/widgets/dialogWidgets/alignmentPane", [], function () {
+define("webodf/editor/widgets/dialogWidgets/alignmentPane", [
+    "webodf/editor/widgets/dialogWidgets/idMangler"],
+function (IdMangler) {
     "use strict";
 
     runtime.loadClass("core.CSSUnits");
 
     var AlignmentPane = function (callback) {
         var self = this,
+            idMangler = new IdMangler(),
             editorSession,
             contentPane,
             form;
@@ -77,7 +80,7 @@ define("webodf/editor/widgets/dialogWidgets/alignmentPane", [], function () {
             }
         };
 
-        this.setEditorSession = function(session) {
+        this.setEditorSession = function (session) {
             editorSession = session;
         };
 
@@ -85,24 +88,24 @@ define("webodf/editor/widgets/dialogWidgets/alignmentPane", [], function () {
             require([
                 "dojo",
                 "dojo/ready",
-                "dojo/dom-construct",
                 "dijit/layout/ContentPane"],
-                function (dojo, ready, domConstruct, ContentPane) {
+                function (dojo, ready, ContentPane) {
                     var editorBase = dojo.config && dojo.config.paths &&
                             dojo.config.paths['webodf/editor'];
-                runtime.assert(editorBase, "webodf/editor path not defined in dojoConfig");
-                ready(function () {
-                    contentPane = new ContentPane({
-                        title: runtime.tr("Alignment"),
-                        href: editorBase+"/widgets/dialogWidgets/alignmentPane.html",
-                        preload: true
+                    runtime.assert(editorBase, "webodf/editor path not defined in dojoConfig");
+                    ready(function () {
+                        contentPane = new ContentPane({
+                            title: runtime.tr("Alignment"),
+                            href: editorBase+"/widgets/dialogWidgets/alignmentPane.html",
+                            preload: true,
+                            ioMethod: idMangler.ioMethod
+                        });
+                        contentPane.onLoad = function () {
+                            form = idMangler.byId('alignmentPaneForm');
+                            runtime.translateContent(form.domNode);
+                        };
+                        return cb();
                     });
-                    contentPane.onLoad = function () {
-                        form = dijit.byId('alignmentPaneForm');
-                        runtime.translateContent(form.domNode);
-                    };
-                    return cb();
-                });
             });
         }
 

+ 9 - 6
main/inc/lib/javascript/wodotexteditor/widgets/dialogWidgets/editHyperlinkPane.js

@@ -26,9 +26,10 @@
 
 define("webodf/editor/widgets/dialogWidgets/editHyperlinkPane", [
     "dojo",
-    "dijit/layout/ContentPane"],
+    "dijit/layout/ContentPane",
+    "webodf/editor/widgets/dialogWidgets/idMangler"],
 
-    function (dojo, ContentPane) {
+    function (dojo, ContentPane, IdMangler) {
         "use strict";
 
         runtime.loadClass("core.CSSUnits");
@@ -36,6 +37,7 @@ define("webodf/editor/widgets/dialogWidgets/editHyperlinkPane", [
         var EditHyperlinkPane = function () {
             var self = this,
                 editorBase = dojo.config && dojo.config.paths && dojo.config.paths['webodf/editor'],
+                idMangler = new IdMangler(),
                 contentPane,
                 form,
                 displayTextField,
@@ -62,12 +64,13 @@ define("webodf/editor/widgets/dialogWidgets/editHyperlinkPane", [
                 title: runtime.tr("editLink"),
                 href: editorBase+"/widgets/dialogWidgets/editHyperlinkPane.html",
                 preload: true,
+                ioMethod: idMangler.ioMethod,
                 onLoad : function () {
-                    form = dijit.byId('editHyperlinkPaneForm');
+                    form = idMangler.byId('editHyperlinkPaneForm');
                     form.onSubmit = onSave;
-                    dijit.byId('cancelHyperlinkChangeButton').onClick = onCancel;
-                    displayTextField = dijit.byId('linkDisplayText');
-                    linkField = dijit.byId('linkUrl');
+                    idMangler.byId('cancelHyperlinkChangeButton').onClick = onCancel;
+                    displayTextField = idMangler.byId('linkDisplayText');
+                    linkField = idMangler.byId('linkUrl');
                     linkField.on("change", function(value) {
                         displayTextField.set('placeHolder', value);
                     });

+ 18 - 13
main/inc/lib/javascript/wodotexteditor/widgets/dialogWidgets/fontEffectsPane.js

@@ -24,11 +24,14 @@
 
 /*global runtime,define,require,document,dijit */
 
-define("webodf/editor/widgets/dialogWidgets/fontEffectsPane", [], function () {
+define("webodf/editor/widgets/dialogWidgets/fontEffectsPane", [
+    "webodf/editor/widgets/dialogWidgets/idMangler"],
+function (IdMangler) {
     "use strict";
 
     var FontEffectsPane = function (callback) {
         var self = this,
+            idMangler = new IdMangler(),
             editorSession,
             contentPane,
             form,
@@ -103,35 +106,36 @@ define("webodf/editor/widgets/dialogWidgets/fontEffectsPane", [], function () {
 
         };
 
+        /*jslint unparam: true*/
         function init(cb) {
             require([
                 "dojo",
                 "dojo/ready",
-                "dojo/dom-construct",
                 "dijit/layout/ContentPane",
-                "dojox/widget/ColorPicker",
+                "dojox/widget/ColorPicker", // referenced in fontEffectsPane.html
                 "webodf/editor/widgets/fontPicker"
-            ], function (dojo, ready, domConstruct, ContentPane, ColorPicker, FontPicker) {
+            ], function (dojo, ready, ContentPane, ColorPicker, FontPicker) {
                 var editorBase = dojo.config && dojo.config.paths &&
                             dojo.config.paths['webodf/editor'];
                 runtime.assert(editorBase, "webodf/editor path not defined in dojoConfig");
                 ready(function () {
                     contentPane = new ContentPane({
                         title: runtime.tr("Font Effects"),
-                        href: editorBase+"/widgets/dialogWidgets/fontEffectsPane.html",
-                        preload: true
+                        href: editorBase + "/widgets/dialogWidgets/fontEffectsPane.html",
+                        preload: true,
+                        ioMethod: idMangler.ioMethod
                     });
 
                     contentPane.onLoad = function () {
-                        var textColorTB = dijit.byId('textColorTB'),
-                            backgroundColorTB = dijit.byId('backgroundColorTB');
+                        var textColorTB = idMangler.byId('textColorTB'),
+                            backgroundColorTB = idMangler.byId('backgroundColorTB');
 
-                        form = dijit.byId('fontEffectsPaneForm');
+                        form = idMangler.byId('fontEffectsPaneForm');
                         runtime.translateContent(form.domNode);
 
-                        preview = document.getElementById('previewText');
-                        textColorPicker = dijit.byId('textColorPicker');
-                        backgroundColorPicker = dijit.byId('backgroundColorPicker');
+                        preview = idMangler.getElementById('previewText');
+                        textColorPicker = idMangler.byId('textColorPicker');
+                        backgroundColorPicker = idMangler.byId('backgroundColorPicker');
 
                         // Bind dojox widgets' values to invisible form elements, for easy parsing
                         textColorPicker.onChange = function (value) {
@@ -143,7 +147,7 @@ define("webodf/editor/widgets/dialogWidgets/fontEffectsPane", [], function () {
 
                         fontPicker = new FontPicker(function (picker) {
                             picker.widget().startup();
-                            document.getElementById('fontPicker').appendChild(picker.widget().domNode);
+                            idMangler.getElementById('fontPicker').appendChild(picker.widget().domNode);
                             picker.widget().name = 'fontName';
                             picker.setEditorSession(editorSession);
                         });
@@ -177,6 +181,7 @@ define("webodf/editor/widgets/dialogWidgets/fontEffectsPane", [], function () {
                 });
             });
         }
+        /*jslint unparam: false*/
 
         this.setEditorSession = function(session) {
             editorSession = session;

+ 90 - 0
main/inc/lib/javascript/wodotexteditor/widgets/dialogWidgets/idMangler.js

@@ -0,0 +1,90 @@
+/**
+ * Copyright (C) 2014 KO GmbH <copyright@kogmbh.com>
+ *
+ * @licstart
+ * This file is part of WebODF.
+ *
+ * WebODF is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License (GNU AGPL)
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * WebODF 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 WebODF.  If not, see <http://www.gnu.org/licenses/>.
+ * @licend
+ *
+ * @source: http://www.webodf.org/
+ * @source: https://github.com/kogmbh/WebODF/
+ */
+
+/*global define, document, dojo, dijit */
+
+define("webodf/editor/widgets/dialogWidgets/idMangler", ["dojo", "dijit"], function (dojo, dijit) {
+    "use strict";
+    var instanceCount = 0;
+
+    /**
+     * Mangles html id attributes and associated references so that the same HTML code can be copied into
+     * a page multiple times without identifier clashes. This also provides helper functions for retrieving
+     * these now-mangled identifiers.
+     * @constructor
+     */
+    function IdMangler() {
+        var suffix;
+
+        /**
+         * Returns the supplied text with identifiers mangled
+         * @param {!string} text
+         * @return {!string}
+         */
+        function mangleIds(text) {
+            /*jslint regexp: true*/
+            var newText = text.replace(/((id|for|data-dojo-id)\s*=\s*["'][^"']+)/g, "$1" + suffix);
+            /*jslint regexp: false*/
+            return newText;
+        }
+        this.mangleIds = mangleIds;
+
+        /**
+         * Replacement method for ContentPane's ioMethod
+         * @return {*} See http://dojotoolkit.org/api/?qs=1.9/dojo/_base/xhr#1_9dojo__base_xhr_get for return details
+         */
+        this.ioMethod = function() {
+            var args = Array.prototype.slice.call(arguments, 0);
+            return dojo.xhr.get.apply(dojo, args).then(function(html) {
+                return mangleIds(html);
+            });
+        };
+
+        /**
+         * Replacement for dijit.byId
+         * @param {!string} id
+         * @return {*} See http://dojotoolkit.org/api/?qs=1.9/dojo/_base/xhr#1_9dijit_registry_byId for return details
+         */
+        this.byId = function(id) {
+            return dijit.byId(id + suffix);
+        };
+
+        /**
+         * Replacement for document.getElementById
+         * @param {!string} id
+         * @return {HTMLElement|*}
+         */
+        this.getElementById = function(id) {
+            return document.getElementById(id + suffix);
+        };
+
+        function init() {
+            suffix = "_" + instanceCount;
+            instanceCount += 1;
+        }
+        init();
+    }
+
+    return IdMangler;
+});

+ 29 - 42
main/inc/lib/javascript/wodotexteditor/widgets/editHyperlinks.js

@@ -22,7 +22,7 @@
  * @source: https://github.com/kogmbh/WebODF/
  */
 
-/*global define,require,document,odf */
+/*global define, require, document, odf, runtime, core, gui */
 
 define("webodf/editor/widgets/editHyperlinks", [
     "webodf/editor/EditorSession",
@@ -35,6 +35,8 @@ define("webodf/editor/widgets/editHyperlinks", [
         "use strict";
 
         runtime.loadClass("odf.OdfUtils");
+        runtime.loadClass("odf.TextSerializer");
+        runtime.loadClass("core.EventSubscriptions");
 
         var EditHyperlinks = function (callback) {
             var self = this,
@@ -44,21 +46,11 @@ define("webodf/editor/widgets/editHyperlinks", [
                 linkEditorContent,
                 editHyperlinkButton,
                 removeHyperlinkButton,
-                odfUtils = new odf.OdfUtils(),
+                odfUtils = odf.OdfUtils,
                 textSerializer = new odf.TextSerializer(),
+                eventSubscriptions = new core.EventSubscriptions(),
                 dialog;
 
-            /**
-             * @param {!Range} selection
-             * @return {!string}
-             */
-            function getTextContent(selection) {
-                var document = selection.startContainer.ownerDocument,
-                    fragmentContainer = document.createElement('span');
-                fragmentContainer.appendChild(selection.cloneContents());
-                return textSerializer.writeToString(fragmentContainer);
-            }
-
             function updateLinkEditorContent() {
                 var selection = editorSession.getSelectedRange(),
                     linksInSelection = editorSession.getSelectedHyperlinks(),
@@ -75,7 +67,8 @@ define("webodf/editor/widgets/editHyperlinks", [
                     // User has selected part of a hyperlink or a block of text. Assume user is attempting to modify the
                     // existing hyperlink, or wants to convert the selection into a hyperlink
                     linkEditorContent.set({
-                        linkDisplayText: getTextContent(selection),
+                        // TODO Improve performance by rewriting to not clone the range contents
+                        linkDisplayText: textSerializer.writeToString(selection.cloneContents()),
                         linkUrl: linkTarget,
                         isReadOnlyText: true
                     });
@@ -89,17 +82,19 @@ define("webodf/editor/widgets/editHyperlinks", [
                 }
             }
 
-            function checkHyperlinkButtons() {
-                var linksInSelection = editorSession.getSelectedHyperlinks();
-
-                // The 3rd parameter is false to avoid firing onChange when setting the value programmatically.
-                removeHyperlinkButton.set('disabled', linksInSelection.length === 0, false);
-            }
+            function updateHyperlinkButtons() {
+                var controllerEnabled = hyperlinkController && hyperlinkController.isEnabled(),
+                    linksInSelection;
 
-            function enableHyperlinkButtons(isEnabled) {
+                // Enable to disable all widgets initially
                 widget.children.forEach(function (element) {
-                    element.setAttribute('disabled', !isEnabled);
+                    element.set('disabled', controllerEnabled !== true, false);
                 });
+                if (controllerEnabled) {
+                    // Specifically enable the remove hyperlink button only if there are links in the current selection
+                    linksInSelection = editorSession.getSelectedHyperlinks();
+                    removeHyperlinkButton.set('disabled', linksInSelection.length === 0, false);
+                }
             }
 
             function updateSelectedLink(hyperlinkData) {
@@ -108,12 +103,12 @@ define("webodf/editor/widgets/editHyperlinks", [
                     selectedLinkRange,
                     linksInSelection = editorSession.getSelectedHyperlinks();
 
-                if (hyperlinkData.isReadOnlyText == "true") {
+                if (hyperlinkData.isReadOnlyText === "true") {
                     if (selection && selection.collapsed && linksInSelection.length === 1) {
                         // Editing the single link the cursor is currently within
                         selectedLinkRange = selection.cloneRange();
                         selectedLinkRange.selectNode(linksInSelection[0]);
-                        selectionController.selectRange(selectedLinkRange, true)
+                        selectionController.selectRange(selectedLinkRange, true);
                     }
                     hyperlinkController.removeHyperlinks();
                     hyperlinkController.addHyperlink(hyperlinkData.linkUrl);
@@ -122,35 +117,27 @@ define("webodf/editor/widgets/editHyperlinks", [
                     linksInSelection = editorSession.getSelectedHyperlinks();
                     selectedLinkRange = selection.cloneRange();
                     selectedLinkRange.selectNode(linksInSelection[0]);
-                    selectionController.selectRange(selectedLinkRange, true)
+                    selectionController.selectRange(selectedLinkRange, true);
                 }
             }
 
             this.setEditorSession = function (session) {
-                if (editorSession) {
-                    editorSession.unsubscribe(EditorSession.signalCursorMoved, checkHyperlinkButtons);
-                    editorSession.unsubscribe(EditorSession.signalParagraphChanged, checkHyperlinkButtons);
-                    editorSession.unsubscribe(EditorSession.signalParagraphStyleModified, checkHyperlinkButtons);
-                    hyperlinkController.unsubscribe(gui.HyperlinkController.enabledChanged, enableHyperlinkButtons);
-                }
-
+                eventSubscriptions.unsubscribeAll();
+                hyperlinkController = undefined;
                 editorSession = session;
                 if (editorSession) {
                     hyperlinkController = editorSession.sessionController.getHyperlinkController();
-
-                    editorSession.subscribe(EditorSession.signalCursorMoved, checkHyperlinkButtons);
-                    editorSession.subscribe(EditorSession.signalParagraphChanged, checkHyperlinkButtons);
-                    editorSession.subscribe(EditorSession.signalParagraphStyleModified, checkHyperlinkButtons);
-                    hyperlinkController.subscribe(gui.HyperlinkController.enabledChanged, enableHyperlinkButtons);
-
-                    enableHyperlinkButtons(hyperlinkController.isEnabled());
-                    checkHyperlinkButtons();
-                } else {
-                    enableHyperlinkButtons( false );
+                    eventSubscriptions.addFrameSubscription(editorSession, EditorSession.signalCursorMoved, updateHyperlinkButtons);
+                    eventSubscriptions.addFrameSubscription(editorSession, EditorSession.signalParagraphChanged, updateHyperlinkButtons);
+                    eventSubscriptions.addFrameSubscription(editorSession, EditorSession.signalParagraphStyleModified, updateHyperlinkButtons);
+                    eventSubscriptions.addSubscription(hyperlinkController, gui.HyperlinkController.enabledChanged, updateHyperlinkButtons);
                 }
+                updateHyperlinkButtons();
             };
 
+            /*jslint emptyblock: true*/
             this.onToolDone = function () {};
+            /*jslint emptyblock: false*/
 
             function init() {
                 textSerializer.filter = new odf.OdfNodeFilter();

+ 10 - 4
main/inc/lib/javascript/wodotexteditor/widgets/fontPicker.js

@@ -23,9 +23,10 @@
  */
 /*global define,require,document */
 define("webodf/editor/widgets/fontPicker", [
-    "dijit/form/Select"],
+    "dijit/form/Select",
+    "dojox/html/entities"],
 
-    function (Select) {
+    function (Select, htmlEntities) {
         "use strict";
 
         /**
@@ -45,6 +46,11 @@ define("webodf/editor/widgets/fontPicker", [
                     width: '150px'
                 }
             });
+            // prevent browser translation service messing up ids
+            select.domNode.setAttribute("translate", "no");
+            select.domNode.classList.add("notranslate");
+            select.dropDown.domNode.setAttribute("translate", "no");
+            select.dropDown.domNode.classList.add("notranslate");
 
             this.widget = function () {
                 return select;
@@ -91,7 +97,7 @@ define("webodf/editor/widgets/fontPicker", [
                     name = documentFonts[i].name;
                     family = documentFonts[i].family || name;
                     selectionList.push({
-                        label: '<span style="font-family: ' + family + ';">' + name + '</span>',
+                        label: '<span style="font-family: ' + htmlEntities.encode(family) + ';">' + htmlEntities.encode(name)+ '</span>',
                         value: name
                     });
                 }
@@ -104,7 +110,7 @@ define("webodf/editor/widgets/fontPicker", [
                 // Lastly populate the fonts provided by the editor
                 for (i = 0; i < editorFonts.length; i += 1) {
                     selectionList.push({
-                        label: '<span style="font-family: ' + editorFonts[i] + ';">' + editorFonts[i] + '</span>',
+                        label: '<span style="font-family: ' + htmlEntities.encode(editorFonts[i]) + ';">' + htmlEntities.encode(editorFonts[i]) + '</span>',
                         value: editorFonts[i]
                     });
                 }

+ 4 - 2
main/inc/lib/javascript/wodotexteditor/widgets/imageInserter.js

@@ -22,7 +22,7 @@
  * @source: https://github.com/kogmbh/WebODF/
  */
 
-/*global define,require,document,Image,FileReader,window,runtime,ops */
+/*global define, require, document, Image, FileReader, window, runtime, ops, gui */
 
 define("webodf/editor/widgets/imageInserter", [
     "dijit/form/Button",
@@ -50,7 +50,7 @@ define("webodf/editor/widgets/imageInserter", [
             function insertImage(mimetype, content, width, height) {
                 textController.removeCurrentSelection();
                 imageController.insertImage(mimetype, content, width, height);
-            };
+            }
 
             /**
              * @param {!string} content  as datauri
@@ -168,7 +168,9 @@ define("webodf/editor/widgets/imageInserter", [
                 }
             };
 
+            /*jslint emptyblock: true*/
             this.onToolDone = function () {};
+            /*jslint emptyblock: false*/
 
             callback(widget);
         };

+ 6 - 5
main/inc/lib/javascript/wodotexteditor/widgets/paragraphAlignment.js

@@ -22,14 +22,14 @@
  * @source: https://github.com/kogmbh/WebODF/
  */
 
-/*global define,require,ops,gui */
+/*global define, require, ops, gui, runtime */
 
 define("webodf/editor/widgets/paragraphAlignment", [
     "dijit/form/ToggleButton",
     "dijit/form/Button",
     "webodf/editor/EditorSession"],
 
-    function (ToggleButton, Button, EditorSession) {
+    function (ToggleButton, Button) {
         "use strict";
 
         var ParagraphAlignment = function (callback) {
@@ -119,8 +119,7 @@ define("webodf/editor/widgets/paragraphAlignment", [
                 justifyRight,
                 justifyFull,
                 outdent,
-                indent
-            ];
+                indent ];
 
             widget.startup = function () {
                 widget.children.forEach(function (element) {
@@ -184,10 +183,12 @@ define("webodf/editor/widgets/paragraphAlignment", [
                 });
             };
 
+            /*jslint emptyblock: true*/
             this.onToolDone = function () {};
+            /*jslint emptyblock: false*/
 
             callback(widget);
         };
 
         return ParagraphAlignment;
-});
+    });

+ 14 - 6
main/inc/lib/javascript/wodotexteditor/widgets/paragraphStyles.js

@@ -22,14 +22,15 @@
  * @source: https://github.com/kogmbh/WebODF/
  */
 
-/*global define,require */
+/*global define, require, runtime, ops */
 
 define("webodf/editor/widgets/paragraphStyles", [
     "dijit/form/Select",
+    "dojox/html/entities",
     "webodf/editor/EditorSession"],
 
-    function (Select, EditorSession) {
-    "use strict"
+    function (Select, htmlEntities, EditorSession) {
+    "use strict";
 
     /**
      * @constructor
@@ -69,7 +70,9 @@ define("webodf/editor/widgets/paragraphStyles", [
         // events
         this.onAdd = null;
         this.onRemove = null;
+        /*jslint emptyblock: true*/
         this.onChange = function () {};
+        /*jslint emptyblock: false*/
 
         function populateStyles() {
             var i, selectionList, availableStyles;
@@ -83,7 +86,7 @@ define("webodf/editor/widgets/paragraphStyles", [
 
             for (i = 0; i < availableStyles.length; i += 1) {
                 selectionList.push({
-                    label: availableStyles[i].displayName || availableStyles[i].name,
+                    label: htmlEntities.encode(availableStyles[i].displayName) || htmlEntities.encode(availableStyles[i].name),
                     value: availableStyles[i].name
                 });
             }
@@ -102,8 +105,8 @@ define("webodf/editor/widgets/paragraphStyles", [
 
             newStyleElement = editorSession.getParagraphStyleElement(styleInfo.name);
             select.addOption({
-                value: styleInfo.name,
-                label: newStyleElement.getAttributeNS(stylens, 'display-name')
+                label: htmlEntities.encode(newStyleElement.getAttributeNS(stylens, 'display-name')),
+                value: styleInfo.name
             });
 
             if (self.onAdd) {
@@ -155,6 +158,11 @@ define("webodf/editor/widgets/paragraphStyles", [
                     width: '100px'
                 }
             });
+            // prevent browser translation service messing up ids
+            select.domNode.setAttribute("translate", "no");
+            select.domNode.classList.add("notranslate");
+            select.dropDown.domNode.setAttribute("translate", "no");
+            select.dropDown.domNode.classList.add("notranslate");
 
             populateStyles();
 

+ 20 - 17
main/inc/lib/javascript/wodotexteditor/widgets/paragraphStylesDialog.js

@@ -22,12 +22,15 @@
  * @source: https://github.com/kogmbh/WebODF/
  */
 
-/*global define,require,dojo,dijit */
+/*global define, require, dojo, dijit, runtime */
 
-define("webodf/editor/widgets/paragraphStylesDialog", [], function () {
+define("webodf/editor/widgets/paragraphStylesDialog", [
+    "webodf/editor/widgets/dialogWidgets/idMangler"],
+function (IdMangler) {
     "use strict";
     return function ParagraphStylesDialog(callback) {
         var self = this,
+            idMangler = new IdMangler(),
             editorSession,
             dialog,
             stylePicker, alignmentPane, fontEffectsPane;
@@ -42,14 +45,11 @@ define("webodf/editor/widgets/paragraphStylesDialog", [], function () {
                 "dijit/layout/ContentPane",
                 "dijit/form/Button",
                 "dijit/form/DropDownButton"], function (Dialog, TooltipDialog, popup, LayoutContainer, TabContainer, ContentPane, Button, DropDownButton) {
-                var i,
-                    tr = runtime.tr,
+                var tr = runtime.tr,
                     mainLayoutContainer,
                     tabContainer,
                     topBar,
                     actionBar,
-                    okButton,
-                    cancelButton,
                     cloneButton,
                     deleteButton,
                     cloneTooltip,
@@ -58,8 +58,7 @@ define("webodf/editor/widgets/paragraphStylesDialog", [], function () {
                     * Mapping of the properties from edit pane properties to the attributes of style:text-properties
                     * @const@type{Array.<!{propertyName:string,attributeName:string,unit:string}>}
                     */
-                    textPropertyMapping = [
-                    {
+                    textPropertyMapping = [{
                         propertyName:  'fontSize',
                         attributeName: 'fo:font-size',
                         unit:          'pt'
@@ -89,8 +88,7 @@ define("webodf/editor/widgets/paragraphStylesDialog", [], function () {
                     * Mapping of the properties from edit pane properties to the attributes of style:paragraph-properties
                     * @const@type{Array.<!{propertyName:string,attributeName:string,unit:string}>}
                     */
-                    paragraphPropertyMapping = [
-                    {
+                    paragraphPropertyMapping = [{
                         propertyName:  'topMargin',
                         attributeName: 'fo:margin-top',
                         unit:          'mm'
@@ -220,9 +218,9 @@ define("webodf/editor/widgets/paragraphStylesDialog", [], function () {
                 mainLayoutContainer.addChild(topBar);
 
                 cloneTooltip = new TooltipDialog({
-                    content:
-                        '<h2 style="margin: 0;">'+tr("Clone this Style")+'</h2><br/>' +
-                        '<label for="name">'+tr("New Name:")+'</label> <input data-dojo-type="dijit/form/TextBox" id="name" name="name"><br/><br/>',
+                    content: idMangler.mangleIds(
+                        '<h2 style="margin: 0;">' + tr("Clone this Style") + '</h2><br/>' +
+                        '<label for="name">' + tr("New Name:") + '</label> <input data-dojo-type="dijit/form/TextBox" id="name" name="name"><br/><br/>'),
                     style: "width: 300px;"
                 });
                 cloneButton = new Button({
@@ -263,11 +261,11 @@ define("webodf/editor/widgets/paragraphStylesDialog", [], function () {
                 actionBar = dojo.create("div", {
                     "class": "dijitDialogPaneActionBar"
                 });
-                okButton = new dijit.form.Button({
+                new dijit.form.Button({
                     label: tr("OK"),
                     onClick: accept
                 }).placeAt(actionBar);
-                cancelButton = new dijit.form.Button({
+                new dijit.form.Button({
                     label: tr("Cancel"),
                     onClick: cancel
                 }).placeAt(actionBar);
@@ -289,7 +287,7 @@ define("webodf/editor/widgets/paragraphStylesDialog", [], function () {
                         stylePicker.widget().domNode.style.marginTop = "5px";
                         topBar.addChild(stylePicker.widget(), 0);
 
-                        stylePicker.onRemove = function (name) {
+                        stylePicker.onRemove = function () {
                             // The style picker automatically falls back
                             // to the first entry if the currently selected
                             // entry is deleted. So it is safe to simply
@@ -319,6 +317,9 @@ define("webodf/editor/widgets/paragraphStylesDialog", [], function () {
                     };
 
                     dialog.onHide = self.onToolDone;
+
+                    // only done to make jslint see the var used
+                    return p || a || f;
                 });
 
                 dialog.addChild(mainLayoutContainer);
@@ -328,7 +329,7 @@ define("webodf/editor/widgets/paragraphStylesDialog", [], function () {
             });
         }
 
-        this.setEditorSession = function(session) {
+        this.setEditorSession = function (session) {
             editorSession = session;
             if (stylePicker) {
                 stylePicker.setEditorSession(session);
@@ -344,7 +345,9 @@ define("webodf/editor/widgets/paragraphStylesDialog", [], function () {
             }
         };
 
+        /*jslint emptyblock: true*/
         this.onToolDone = function () {};
+        /*jslint emptyblock: false*/
 
         // init
         makeWidget(function (dialog) {

+ 17 - 23
main/inc/lib/javascript/wodotexteditor/widgets/simpleStyles.js

@@ -22,18 +22,17 @@
  * @source: https://github.com/kogmbh/WebODF/
  */
 
-/*global define,require,gui,ops */
+/*global define, require, runtime, gui, ops */
 
 define("webodf/editor/widgets/simpleStyles", [
     "webodf/editor/widgets/fontPicker",
     "dijit/form/ToggleButton",
-    "dijit/form/NumberSpinner",
-    "webodf/editor/EditorSession"],
+    "dijit/form/NumberSpinner"],
 
-    function (FontPicker, ToggleButton, NumberSpinner, EditorSession) {
+    function (FontPicker, ToggleButton, NumberSpinner) {
         "use strict";
 
-        var SimpleStyles = function(callback) {
+        var SimpleStyles = function (callback) {
             var self = this,
                 editorSession,
                 widget = {},
@@ -100,7 +99,7 @@ define("webodf/editor/widgets/simpleStyles", [
                 showLabel: false,
                 value: 12,
                 smallDelta: 1,
-                constraints: {min:6, max:96},
+                constraints: {min: 6, max: 96},
                 intermediateChanges: true,
                 onChange: function (value) {
                     directFormattingController.setFontSize(value);
@@ -116,10 +115,12 @@ define("webodf/editor/widgets/simpleStyles", [
                 }
             });
 
+            /*jslint emptyblock: true*/
             fontPicker = new FontPicker(function () {});
+            /*jslint emptyblock: false*/
             fontPickerWidget = fontPicker.widget();
             fontPickerWidget.setAttribute('disabled', true);
-            fontPickerWidget.onChange = function(value) {
+            fontPickerWidget.onChange = function (value) {
                 directFormattingController.setFontName(value);
                 self.onToolDone();
             };
@@ -141,16 +142,16 @@ define("webodf/editor/widgets/simpleStyles", [
             function updateStyleButtons(changes) {
                 // The 3rd parameter to set(...) is false to avoid firing onChange when setting the value programmatically.
                 var updateCalls = {
-                    isBold: function(value) { boldButton.set('checked', value, false); },
-                    isItalic: function(value) { italicButton.set('checked', value, false); },
-                    hasUnderline: function(value) { underlineButton.set('checked', value, false); },
-                    hasStrikeThrough: function(value) { strikethroughButton.set('checked', value, false); },
-                    fontSize: function(value) { 
+                    isBold: function (value) { boldButton.set('checked', value, false); },
+                    isItalic: function (value) { italicButton.set('checked', value, false); },
+                    hasUnderline: function (value) { underlineButton.set('checked', value, false); },
+                    hasStrikeThrough: function (value) { strikethroughButton.set('checked', value, false); },
+                    fontSize: function (value) { 
                         fontSizeSpinner.set('intermediateChanges', false); // Necessary due to https://bugs.dojotoolkit.org/ticket/11588
                         fontSizeSpinner.set('value', value, false);
                         fontSizeSpinner.set('intermediateChanges', true);
                     },
-                    fontName: function(value) { fontPickerWidget.set('value', value, false); }
+                    fontName: function (value) { fontPickerWidget.set('value', value, false); }
                 };
 
                 Object.keys(changes).forEach(function (key) {
@@ -167,16 +168,8 @@ define("webodf/editor/widgets/simpleStyles", [
                 });
             }
 
-            function handleCursorMoved(cursor) {
-                if (directFormattingController.enabledFeatures().directTextStyling) {
-                    var disabled = cursor.getSelectionType() === ops.OdtCursor.RegionSelection;
-                    enableStyleButtons({ directTextStyling: !disabled });
-                }
-            }
-
-            this.setEditorSession = function(session) {
+            this.setEditorSession = function (session) {
                 if (editorSession) {
-                    editorSession.unsubscribe(EditorSession.signalCursorMoved, handleCursorMoved);
                     directFormattingController.unsubscribe(gui.DirectFormattingController.textStylingChanged, updateStyleButtons);
                     directFormattingController.unsubscribe(gui.DirectFormattingController.enabledChanged, enableStyleButtons);
                 }
@@ -186,7 +179,6 @@ define("webodf/editor/widgets/simpleStyles", [
                 if (editorSession) {
                     directFormattingController = editorSession.sessionController.getDirectFormattingController();
 
-                    editorSession.subscribe(EditorSession.signalCursorMoved, handleCursorMoved);
                     directFormattingController.subscribe(gui.DirectFormattingController.textStylingChanged, updateStyleButtons);
                     directFormattingController.subscribe(gui.DirectFormattingController.enabledChanged, enableStyleButtons);
 
@@ -205,7 +197,9 @@ define("webodf/editor/widgets/simpleStyles", [
                 });
             };
 
+            /*jslint emptyblock: true*/
             this.onToolDone = function () {};
+            /*jslint emptyblock: false*/
 
             callback(widget);
         };

+ 47 - 46
main/inc/lib/javascript/wodotexteditor/widgets/toolbarWidgets/currentStyle.js

@@ -27,62 +27,63 @@
 define("webodf/editor/widgets/toolbarWidgets/currentStyle",
        ["webodf/editor/EditorSession"],
 
-  function (EditorSession) {
-    "use strict";
+    function (EditorSession) {
+        "use strict";
 
-    return function CurrentStyle(callback) {
-        var self = this,
-            editorSession,
-            paragraphStyles;
+        return function CurrentStyle(callback) {
+            var self = this,
+                editorSession,
+                paragraphStyles;
 
-        function selectParagraphStyle(info) {
-            if (paragraphStyles) {
-                if (info.type === 'style') {
-                    paragraphStyles.setValue(info.styleName);
+            function selectParagraphStyle(info) {
+                if (paragraphStyles) {
+                    if (info.type === 'style') {
+                        paragraphStyles.setValue(info.styleName);
+                    }
                 }
             }
-        }
 
-        function setParagraphStyle() {
-            if (editorSession) {
-                editorSession.setCurrentParagraphStyle(paragraphStyles.value());
+            function setParagraphStyle() {
+                if (editorSession) {
+                    editorSession.setCurrentParagraphStyle(paragraphStyles.value());
+                }
+                self.onToolDone();
             }
-            self.onToolDone();
-        }
-
-        function makeWidget(callback) {
-            require(["webodf/editor/widgets/paragraphStyles"], function (ParagraphStyles) {
-                var p;
 
-                p = new ParagraphStyles(function (pStyles) {
-                    paragraphStyles = pStyles;
+            function makeWidget(callback) {
+                require(["webodf/editor/widgets/paragraphStyles"], function (ParagraphStyles) {
+                    var p = new ParagraphStyles(function (pStyles) {
+                        paragraphStyles = pStyles;
 
-                    paragraphStyles.widget().onChange = setParagraphStyle;
+                        paragraphStyles.widget().onChange = setParagraphStyle;
 
-                    paragraphStyles.setEditorSession(editorSession);
-                    return callback(paragraphStyles.widget());
+                        paragraphStyles.setEditorSession(editorSession);
+                        return callback(paragraphStyles.widget());
+                    });
+                    return p; // make sure p is not unused
                 });
-            });
-        }
-
-        this.setEditorSession = function(session) {
-            if (editorSession) {
-                editorSession.unsubscribe(EditorSession.signalParagraphChanged, selectParagraphStyle);
-            }
-            editorSession = session;
-            if (paragraphStyles) {
-                paragraphStyles.setEditorSession(editorSession);
             }
-            if (editorSession) {
-                editorSession.subscribe(EditorSession.signalParagraphChanged, selectParagraphStyle);
-                // TODO: selectParagraphStyle(editorSession.getCurrentParagraphStyle());
-            }
-        };
 
-        this.onToolDone = function () {};
+            this.setEditorSession = function (session) {
+                if (editorSession) {
+                    editorSession.unsubscribe(EditorSession.signalParagraphChanged, selectParagraphStyle);
+                }
+                editorSession = session;
+                if (paragraphStyles) {
+                    paragraphStyles.setEditorSession(editorSession);
+                }
+                if (editorSession) {
+                    editorSession.subscribe(EditorSession.signalParagraphChanged, selectParagraphStyle);
+                    // TODO: selectParagraphStyle(editorSession.getCurrentParagraphStyle());
+                }
+            };
 
-        makeWidget(function (widget) {
-            return callback(widget);
-        });
-    };
-});
+            /*jslint emptyblock: true*/
+            this.onToolDone = function () {};
+            /*jslint emptyblock: false*/
+
+            makeWidget(function (widget) {
+                return callback(widget);
+            });
+        };
+    });

+ 4 - 2
main/inc/lib/javascript/wodotexteditor/widgets/undoRedoMenu.js

@@ -22,7 +22,7 @@
  * @source: https://github.com/kogmbh/WebODF/
  */
 
-/*global define,require*/
+/*global define, require, runtime*/
 
 define("webodf/editor/widgets/undoRedoMenu",
     ["webodf/editor/EditorSession", "dijit/form/Button"],
@@ -86,7 +86,7 @@ define("webodf/editor/widgets/undoRedoMenu",
                 }
             }
 
-            this.setEditorSession = function(session) {
+            this.setEditorSession = function (session) {
                 if (editorSession) {
                     editorSession.unsubscribe(EditorSession.signalUndoStackChanged, checkUndoButtons);
                 }
@@ -102,7 +102,9 @@ define("webodf/editor/widgets/undoRedoMenu",
                 }
             };
 
+            /*jslint emptyblock: true*/
             this.onToolDone = function () {};
+            /*jslint emptyblock: false*/
 
             // init
             callback(widget);

+ 55 - 55
main/inc/lib/javascript/wodotexteditor/widgets/zoomSlider.js

@@ -22,72 +22,72 @@
  * @source: https://github.com/kogmbh/WebODF/
  */
 
-/*global define,require*/
+/*global define, require, gui*/
 
 define("webodf/editor/widgets/zoomSlider", [
-    "dijit/form/HorizontalSlider",
-    "dijit/form/NumberTextBox",
-    "webodf/editor/EditorSession"],
+    "dijit/form/HorizontalSlider"],
 
-    function (HorizontalSlider, NumberTextBox, EditorSession) {
-    "use strict";
+    function (HorizontalSlider) {
+        "use strict";
 
-    // The slider zooms from -1 to +1, which corresponds
-    // to zoom levels of 1/extremeZoomFactor to extremeZoomFactor.
-    return function ZoomSlider(callback) {
-        var self = this,
-            editorSession,
-            slider,
-            extremeZoomFactor = 4;
+        // The slider zooms from -1 to +1, which corresponds
+        // to zoom levels of 1/extremeZoomFactor to extremeZoomFactor.
+        return function ZoomSlider(callback) {
+            var self = this,
+                editorSession,
+                slider,
+                extremeZoomFactor = 4;
 
-        function updateSlider(zoomLevel) {
-            slider.set('value', Math.log(zoomLevel) / Math.log(extremeZoomFactor), false);
-        }
-
-        this.setEditorSession = function(session) {
-            var zoomHelper;
-            if (editorSession) {
-                editorSession.getOdfCanvas().getZoomHelper().unsubscribe(gui.ZoomHelper.signalZoomChanged, updateSlider);
-            }
-
-            editorSession = session;
-            if (editorSession) {
-                zoomHelper = editorSession.getOdfCanvas().getZoomHelper();
-                zoomHelper.subscribe(gui.ZoomHelper.signalZoomChanged, updateSlider);
-                updateSlider(zoomHelper.getZoomLevel());
+            function updateSlider(zoomLevel) {
+                slider.set('value', Math.log(zoomLevel) / Math.log(extremeZoomFactor), false);
             }
-            slider.setAttribute('disabled', !editorSession);
-        };
-
-        this.onToolDone = function () {};
 
-        // init
-        function init() {
-            slider = new HorizontalSlider({
-                name: 'zoomSlider',
-                disabled: true,
-                value: 0,
-                minimum: -1,
-                maximum: 1,
-                discreteValues: 0.01,
-                intermediateChanges: true,
-                style: {
-                    width: '150px',
-                    height: '25px',
-                    float: 'right'
+            this.setEditorSession = function (session) {
+                var zoomHelper;
+                if (editorSession) {
+                    editorSession.getOdfCanvas().getZoomHelper().unsubscribe(gui.ZoomHelper.signalZoomChanged, updateSlider);
                 }
-            });
 
-            slider.onChange = function (value) {
+                editorSession = session;
                 if (editorSession) {
-                    editorSession.getOdfCanvas().getZoomHelper().setZoomLevel(Math.pow(extremeZoomFactor, value));
+                    zoomHelper = editorSession.getOdfCanvas().getZoomHelper();
+                    zoomHelper.subscribe(gui.ZoomHelper.signalZoomChanged, updateSlider);
+                    updateSlider(zoomHelper.getZoomLevel());
                 }
-                self.onToolDone();
+                slider.setAttribute('disabled', !editorSession);
             };
 
-            return callback(slider);
-        }
+            /*jslint emptyblock: true*/
+            this.onToolDone = function () {};
+            /*jslint emptyblock: false*/
 
-        init();
-    };
-});
+            // init
+            function init() {
+                slider = new HorizontalSlider({
+                    name: 'zoomSlider',
+                    disabled: true,
+                    value: 0,
+                    minimum: -1,
+                    maximum: 1,
+                    discreteValues: 0.01,
+                    intermediateChanges: true,
+                    style: {
+                        width: '150px',
+                        height: '25px',
+                        float: 'right'
+                    }
+                });
+
+                slider.onChange = function (value) {
+                    if (editorSession) {
+                        editorSession.getOdfCanvas().getZoomHelper().setZoomLevel(Math.pow(extremeZoomFactor, value));
+                    }
+                    self.onToolDone();
+                };
+
+                return callback(slider);
+            }
+
+            init();
+        };
+    });

+ 17 - 0
main/inc/lib/javascript/wodotexteditor/wodotexteditor.css

@@ -75,6 +75,23 @@
    text-align: center;
 }
 
+.webodfeditor-dijitSaveAsIcon {
+   background-image: url("images/saveas.png");
+   background-repeat: no-repeat;
+   width: 18px;
+   height: 18px;
+   text-align: center;
+}
+
+/* Fix toolbar not adapting size to floating toolbar elements */
+.dijitToolbar:after {
+   content: ".";
+   visibility: hidden;
+   display: block;
+   height: 0;
+   clear: both;
+}
+
 .dijitDialog {
     border: none !important;
     box-shadow: 0 1px 50px rgba(0, 0, 0, 0.25) !important;

+ 135 - 53
main/inc/lib/javascript/wodotexteditor/wodotexteditor.js

@@ -22,16 +22,17 @@
  * @source: https://github.com/kogmbh/WebODF/
  */
 
+/*global window, document, alert, navigator, require, dojo, runtime, core, gui, ops, odf, WodoFromSource*/
+
 /**
  * Namespace of the Wodo.TextEditor
  * @namespace
+ * @name Wodo
  */
-var Wodo = Wodo || (function () {
+window.Wodo = window.Wodo || (function () {
     "use strict";
 
-    var installationPath = (function() {
-        "use strict";
-
+    function getInstallationPath() {
         /**
          * Sees to get the url of this script on top of the stack trace.
          * @param {!string|undefined} stack
@@ -41,7 +42,9 @@ var Wodo = Wodo || (function () {
             var url, matches;
 
             if (typeof stack === "string" && stack) {
+                /*jslint regexp: true*/
                 matches = stack.match(/((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/);
+                /*jslint regexp: false*/
                 url = matches && matches[1];
             }
             if (typeof url === "string" && url) {
@@ -56,9 +59,7 @@ var Wodo = Wodo || (function () {
          * @return {!string|undefined}
          */
         function getCurrentScriptElementSrcByTricks() {
-            var i,
-                pageUrl = window.location.href,
-                scriptElements = document.getElementsByTagName("script");
+            var scriptElements = document.getElementsByTagName("script");
 
             // if there is only one script, it must be this
             if (scriptElements.length === 1) {
@@ -68,8 +69,7 @@ var Wodo = Wodo || (function () {
             // otherwise get it from the stacktrace
             try {
                 throw new Error();
-            }
-            catch (err) {
+            } catch (err) {
                 return getScriptUrlFromStack(err.stack);
             }
         }
@@ -87,6 +87,11 @@ var Wodo = Wodo || (function () {
             a = document.createElement('a');
             a.href = scriptElementSrc;
             pathname = a.pathname;
+            if (pathname.charAt(0) !== "/") {
+                // Various versions of Internet Explorer seems to neglect the leading slash under some conditions
+                // (not when watching it with the dev tools of course!). This was confirmed in IE10 + IE11
+                pathname = "/" + pathname;
+            }
 
             pos = pathname.lastIndexOf("/");
             if (pos !== -1) {
@@ -96,28 +101,12 @@ var Wodo = Wodo || (function () {
             alert("Could not estimate installation path of the Wodo.TextEditor.");
         }
         return path;
-    }());
-
-    window.dojoConfig = (function() {
-        var WebODFEditorDojoLocale = "C";
-
-        if (navigator && navigator.language && navigator.language.match(/^(de)/)) {
-            WebODFEditorDojoLocale = navigator.language.substr(0, 2);
-        }
-
-        return {
-            locale: WebODFEditorDojoLocale,
-            paths: {
-                "webodf/editor": installationPath,
-                "dijit":         installationPath + "/dijit",
-                "dojox":         installationPath + "/dojox",
-                "dojo":          installationPath + "/dojo",
-                "resources":     installationPath + "/resources"
-            }
-        };
-    }());
+    }
 
-    var /** @inner @type{!boolean} */
+    var /** @inner @const
+            @type{!string} */
+        installationPath = getInstallationPath(),
+        /** @inner @type{!boolean} */
         isInitalized = false,
         /** @inner @type{!Array.<!function():undefined>} */
         pendingInstanceCreationCalls = [],
@@ -139,13 +128,40 @@ var Wodo = Wodo || (function () {
         memberId = "localuser",
         // constructors
         BorderContainer, ContentPane, FullWindowZoomHelper, EditorSession, Tools,
+        /** @inner @const
+            @type{!string} */
+        MODUS_FULLEDITING = "fullediting",
+        /** @inner @const
+            @type{!string} */
+        MODUS_REVIEW = "review",
         /** @inner @const
             @type{!string} */
         EVENT_UNKNOWNERROR = "unknownError",
+        /** @inner @const
+            @type {!string} */
+        EVENT_DOCUMENTMODIFIEDCHANGED = "documentModifiedChanged",
         /** @inner @const
             @type {!string} */
         EVENT_METADATACHANGED = "metadataChanged";
 
+    window.dojoConfig = (function () {
+        var WebODFEditorDojoLocale = "C";
+
+        if (navigator && navigator.language && navigator.language.match(/^(de)/)) {
+            WebODFEditorDojoLocale = navigator.language.substr(0, 2);
+        }
+
+        return {
+            locale: WebODFEditorDojoLocale,
+            paths: {
+                "webodf/editor": installationPath,
+                "dijit":         installationPath + "/dijit",
+                "dojox":         installationPath + "/dojox",
+                "dojo":          installationPath + "/dojo",
+                "resources":     installationPath + "/resources"
+            }
+        };
+    }());
 
     /**
      * @return {undefined}
@@ -166,7 +182,7 @@ var Wodo = Wodo || (function () {
 
                 BorderContainer = BC;
                 ContentPane = CP;
-                FullWindowZoomHelper = FWZH,
+                FullWindowZoomHelper = FWZH;
                 EditorSession = ES;
                 Tools = T;
 
@@ -198,6 +214,9 @@ var Wodo = Wodo || (function () {
                     isInitalized = true;
                     pendingInstanceCreationCalls.forEach(function (create) { create(); });
                 });
+
+                // only done to make jslint see the var used
+                return t;
             }
         );
     }
@@ -242,8 +261,7 @@ var Wodo = Wodo || (function () {
             return editorOptions.allFeaturesEnabled ? (isFeatureEnabled !== false) : isFeatureEnabled;
         }
 
-        var self = this,
-            userData,
+        var userData,
             //
             mainContainerElement = document.getElementById(mainContainerElementId),
             canvasElement,
@@ -275,15 +293,17 @@ var Wodo = Wodo || (function () {
             //
             loadOdtFile = editorOptions.loadCallback,
             saveOdtFile = editorOptions.saveCallback,
+            saveAsOdtFile = editorOptions.saveAsCallback,
+            downloadOdtFile = editorOptions.downloadCallback,
             close =       editorOptions.closeCallback,
             //
+            reviewModeEnabled = (editorOptions.modus === MODUS_REVIEW),
             directTextStylingEnabled = isEnabled(editorOptions.directTextStylingEnabled),
             directParagraphStylingEnabled = isEnabled(editorOptions.directParagraphStylingEnabled),
-            paragraphStyleSelectingEnabled = isEnabled(editorOptions.paragraphStyleSelectingEnabled),
-            paragraphStyleEditingEnabled = isEnabled(editorOptions.paragraphStyleEditingEnabled),
-            imageEditingEnabled = isEnabled(editorOptions.imageEditingEnabled),
+            paragraphStyleSelectingEnabled = (!reviewModeEnabled) && isEnabled(editorOptions.paragraphStyleSelectingEnabled),
+            paragraphStyleEditingEnabled =   (!reviewModeEnabled) && isEnabled(editorOptions.paragraphStyleEditingEnabled),
+            imageEditingEnabled =            (!reviewModeEnabled) && isEnabled(editorOptions.imageEditingEnabled),
             hyperlinkEditingEnabled = isEnabled(editorOptions.hyperlinkEditingEnabled),
-            reviewModeEnabled = Boolean(editorOptions.reviewModeEnabled), // needs to be explicitly enabled
             annotationsEnabled = reviewModeEnabled || isEnabled(editorOptions.annotationsEnabled),
             undoRedoEnabled = isEnabled(editorOptions.undoRedoEnabled),
             zoomingEnabled = isEnabled(editorOptions.zoomingEnabled),
@@ -293,10 +313,11 @@ var Wodo = Wodo || (function () {
             //
             eventNotifier = new core.EventNotifier([
                 EVENT_UNKNOWNERROR,
+                EVENT_DOCUMENTMODIFIEDCHANGED,
                 EVENT_METADATACHANGED
             ]);
 
-        runtime.assert(Boolean(mainContainerElement), "No id of an existing element passed to Wodo.createTextEditor(): "+mainContainerElementId);
+        runtime.assert(Boolean(mainContainerElement), "No id of an existing element passed to Wodo.createTextEditor(): " + mainContainerElementId);
 
         /**
          * @param {!Object} changes
@@ -306,6 +327,14 @@ var Wodo = Wodo || (function () {
             eventNotifier.emit(EVENT_METADATACHANGED, changes);
         }
 
+        /**
+         * @param {!Object} changes
+         * @return {undefined}
+         */
+        function relayModifiedSignal(modified) {
+            eventNotifier.emit(EVENT_DOCUMENTMODIFIEDCHANGED, modified);
+        }
+
         /**
          * @return {undefined}
          */
@@ -332,6 +361,7 @@ var Wodo = Wodo || (function () {
             });
             if (undoRedoEnabled) {
                 editorSession.sessionController.setUndoManager(new gui.TrivialUndoManager());
+                editorSession.sessionController.getUndoManager().subscribe(gui.UndoManager.signalDocumentModifiedChanged, relayModifiedSignal);
             }
 
             // Relay any metadata changes to the Editor's consumer as an event
@@ -374,7 +404,7 @@ var Wodo = Wodo || (function () {
          * @param {!function(!Error=):undefined} callback Called once the document has been opened, passes an error object in case of error
          * @return {undefined}
          */
-        this.openDocumentFromUrl = function(docUrl, editorReadyCallback) {
+        this.openDocumentFromUrl = function (docUrl, editorReadyCallback) {
             runtime.assert(docUrl, "document should be defined here.");
             runtime.assert(!pendingEditorReadyCallback, "pendingEditorReadyCallback should not exist here.");
             runtime.assert(!editorSession, "editorSession should not exist here.");
@@ -395,7 +425,7 @@ var Wodo = Wodo || (function () {
             };
 
             odfCanvas.load(docUrl);
-        }
+        };
 
         /**
          * Closes the document, and does cleanup.
@@ -404,7 +434,7 @@ var Wodo = Wodo || (function () {
          * @param {!function(!Error=):undefined} callback  Called once the document has been closed, passes an error object in case of error
          * @return {undefined}
          */
-        this.closeDocument = function(callback) {
+        this.closeDocument = function (callback) {
             runtime.assert(session, "session should exist here.");
 
             endEditing();
@@ -437,7 +467,7 @@ var Wodo = Wodo || (function () {
                     });
                 }
             });
-        }
+        };
 
         /**
          * @name TextEditor#getDocumentAsByteArray
@@ -445,19 +475,19 @@ var Wodo = Wodo || (function () {
          * @param {!function(err:?Error, file:!Uint8Array=):undefined} callback Called with the current document as ODT file as bytearray, passes an error object in case of error
          * @return {undefined}
          */
-        this.getDocumentAsByteArray = function(callback) {
+        this.getDocumentAsByteArray = function (callback) {
             var odfContainer = odfCanvas.odfContainer();
 
             if (odfContainer) {
-                odfContainer.createByteArray(function(ba) {
+                odfContainer.createByteArray(function (ba) {
                     callback(null, ba);
-                }, function(errorString) {
-                    callback(new Error(errorString ? errorString : "Could not create bytearray from OdfContainer."));
+                }, function (errorString) {
+                    callback(new Error(errorString || "Could not create bytearray from OdfContainer."));
                 });
             } else {
                 callback(new Error("No odfContainer set!"));
             }
-        }
+        };
 
         /**
          * Sets the metadata fields from the given properties map.
@@ -479,7 +509,7 @@ var Wodo = Wodo || (function () {
          * @param {?Array.<!string>} removedProperties An array of metadata field names (prefixed).
          * @return {undefined}
          */
-        this.setMetadata = function(setProperties, removedProperties) {
+        this.setMetadata = function (setProperties, removedProperties) {
             runtime.assert(editorSession, "editorSession should exist here.");
 
             editorSession.sessionController.getMetadataController().setMetadata(setProperties, removedProperties);
@@ -493,7 +523,7 @@ var Wodo = Wodo || (function () {
          * dc:creator
          * @return {?string}
          */
-        this.getMetadata = function(property) {
+        this.getMetadata = function (property) {
             runtime.assert(editorSession, "editorSession should exist here.");
 
             return editorSession.sessionController.getMetadataController().getMetadata(property);
@@ -520,9 +550,45 @@ var Wodo = Wodo || (function () {
          * @function
          * @return {!Object.<!string,!string>}
          */
-        this.getUserData = function() {
+        this.getUserData = function () {
             return cloneUserData(userData);
-        }
+        };
+
+        /**
+         * Sets the current state of the document to be either the unmodified state
+         * or a modified state.
+         * If @p modified is @true and the current state was already a modified state,
+         * this call has no effect and also does not remove the unmodified flag
+         * from the state which has it set.
+         *
+         * @name TextEditor#setDocumentModified
+         * @function
+         * @param {!boolean} modified
+         * @return {undefined}
+         */
+        this.setDocumentModified = function (modified) {
+            runtime.assert(editorSession, "editorSession should exist here.");
+
+            if (undoRedoEnabled) {
+                editorSession.sessionController.getUndoManager().setDocumentModified(modified);
+            }
+        };
+
+        /**
+         * Returns if the current state of the document matches the unmodified state.
+         * @name TextEditor#isDocumentModified
+         * @function
+         * @return {!boolean}
+         */
+        this.isDocumentModified = function () {
+            runtime.assert(editorSession, "editorSession should exist here.");
+
+            if (undoRedoEnabled) {
+                return editorSession.sessionController.getUndoManager().isDocumentModified();
+            }
+
+            return false;
+        };
 
         /**
          * @return {undefined}
@@ -548,7 +614,7 @@ var Wodo = Wodo || (function () {
          * @param {!function(!Error=):undefined} callback Called once the destruction has been completed, passes an error object in case of error
          * @return {undefined}
          */
-        this.destroy = function(callback) {
+        this.destroy = function (callback) {
             var destroyCallbacks = [];
 
             // TODO: decide if some forced close should be done here instead of enforcing proper API usage
@@ -565,7 +631,7 @@ var Wodo = Wodo || (function () {
             ]);
 
             core.Async.destroyAll(destroyCallbacks, callback);
-        }
+        };
 
         // TODO:
         // this.openDocumentFromByteArray = openDocumentFromByteArray; see also https://github.com/kogmbh/WebODF/issues/375
@@ -635,6 +701,11 @@ var Wodo = Wodo || (function () {
             // to style also all dialogs, which are attached directly to body
             document.body.classList.add("claro");
 
+            // prevent browser translation service messing up internal address system
+            // TODO: this should be done more centrally, but where exactly?
+            canvasElement.setAttribute("translate", "no");
+            canvasElement.classList.add("notranslate");
+
             // create widgets
             mainContainer = new BorderContainer({}, mainContainerElementId);
 
@@ -649,6 +720,8 @@ var Wodo = Wodo || (function () {
                 onToolDone: setFocusToOdfCanvas,
                 loadOdtFile: loadOdtFile,
                 saveOdtFile: saveOdtFile,
+                saveAsOdtFile: saveAsOdtFile,
+                downloadOdtFile: downloadOdtFile,
                 close: close,
                 directTextStylingEnabled: directTextStylingEnabled,
                 directParagraphStylingEnabled: directParagraphStylingEnabled,
@@ -717,8 +790,11 @@ var Wodo = Wodo || (function () {
      * @function
      * @param {!string} editorContainerElementId id of the existing div element which will contain the editor (should be empty before)
      * @param editorOptions options to configure the features of the editor. All entries are optional
+     * @param [editorOptions.modus=Wodo.MODUS_FULLEDITING] set the editing modus. Current options: Wodo.MODUS_FULLEDITING, Wodo.MODUS_REVIEW
      * @param [editorOptions.loadCallback] parameter-less callback method, adds a "Load" button to the toolbar which triggers this method
      * @param [editorOptions.saveCallback] parameter-less callback method, adds a "Save" button to the toolbar which triggers this method
+     * @param [editorOptions.saveAsCallback] parameter-less callback method, adds a "Save as" button to the toolbar which triggers this method
+     * @param [editorOptions.downloadCallback] parameter-less callback method, adds a "Download" button to the right of the toolbar which triggers this method
      * @param [editorOptions.closeCallback] parameter-less callback method, adds a "Save" button to the toolbar which triggers this method
      * @param [editorOptions.allFeaturesEnabled=false] if set to 'true', switches the default for all features from 'false' to 'true'
      * @param [editorOptions.directTextStylingEnabled=false] if set to 'true', enables the direct styling of text (e.g. bold/italic or font)
@@ -767,8 +843,14 @@ var Wodo = Wodo || (function () {
     return {
         createTextEditor: createTextEditor,
         // flags
+        /** Id of full editing modus */
+        MODUS_FULLEDITING: MODUS_FULLEDITING,
+        /** Id of review modus */
+        MODUS_REVIEW: MODUS_REVIEW,
         /** Id of event for an unkown error */
         EVENT_UNKNOWNERROR: EVENT_UNKNOWNERROR,
+        /** Id of event if documentModified state changes */
+        EVENT_DOCUMENTMODIFIEDCHANGED: EVENT_DOCUMENTMODIFIEDCHANGED,
         /** Id of event if metadata changes */
         EVENT_METADATACHANGED: EVENT_METADATACHANGED
     };

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor