diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b6b4a6a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "i18n"] + path = i18n + url = https://github.com/WMEValidator/i18n.git diff --git a/00.config.sh b/00.config.sh new file mode 100644 index 0000000..923b98d --- /dev/null +++ b/00.config.sh @@ -0,0 +1,12 @@ +## Path to Closure Compiler +COMPILER=${HOME}/bin/compiler.jar + +EXT_NAME=WV +EXT_FILE_NAME="WME_Validator" + +EXT_VER="1.1.20" +CUR_VER="git" + +SRC_DIR="." +DST_DIR="build" +TMP_DIR="build/tmp" diff --git a/10.release.sh b/10.release.sh new file mode 100755 index 0000000..57c94ad --- /dev/null +++ b/10.release.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +. ./00.config.sh + +EXTRA_PARAMS="--compilation_level ADVANCED_OPTIMIZATIONS" + +. ./99.build.sh diff --git a/20.debug.sh b/20.debug.sh new file mode 100755 index 0000000..b5b6a73 --- /dev/null +++ b/20.debug.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +. ./00.config.sh + +EXTRA_PARAMS="--define DEF_DEBUG=true \ + --compilation_level SIMPLE_OPTIMIZATIONS \ + --formatting PRETTY_PRINT" + +. ./99.build.sh diff --git a/99.build.sh b/99.build.sh new file mode 100644 index 0000000..fc768a3 --- /dev/null +++ b/99.build.sh @@ -0,0 +1,71 @@ +#!/bin/sh + +if [ -z "${SRC_DIR}" ]; then + echo "No config found" + exit 1 +fi + +mkdir -p "${TMP_DIR}" + +LOC_FILE="${TMP_DIR}/gen-i18n.js" +rm -f "${LOC_FILE}" + +## Generate localization file +cat "${SRC_DIR}/meta/i18n-begin.js" > "${LOC_FILE}" +printf "Localizations:" +for FILE in $(ls -r ${SRC_DIR}/i18n/*.js); do + CODE="${FILE##*/}" + CODE="${CODE%%.js}" + if [ "${CODE}" = "default" ]; then + CODE="EN" + fi + printf " ${CODE}" + echo "\"${CODE}\": {" >> "${LOC_FILE}" + echo "\".codeISO\": \"${CODE}\"," >> "${LOC_FILE}" + cat "${FILE}" >> "${LOC_FILE}" + echo "}, // ${CODE}" >> "${LOC_FILE}" +done +echo +cat "${SRC_DIR}/meta/i18n-end.js" >> "${LOC_FILE}" + +java -jar "${COMPILER}" \ + --language_in ECMASCRIPT5 \ + --js "${SRC_DIR}/src/release.js" \ + --js "${LOC_FILE}" \ + --js "${SRC_DIR}/src/enc.js" \ + --js "${SRC_DIR}/src/helpers.js" \ + --js "${SRC_DIR}/src/data.js" \ + --js "${SRC_DIR}/src/basic.js" \ + --js "${SRC_DIR}/src/report.js" \ + --js "${SRC_DIR}/src/validate.js" \ + --js "${SRC_DIR}/src/login.js" \ + --js "${SRC_DIR}/src/encrypted.js" \ + --js "${SRC_DIR}/src/lib/i18n.js" \ + --js "${SRC_DIR}/src/lib/audio.js" \ + --js "${SRC_DIR}/src/lib/thui.js" \ + --js "${SRC_DIR}/src/lib/compressor.js" \ + --js "${SRC_DIR}/src/ext/tea-block.js" \ + --js "${SRC_DIR}/src/ext/base64.js" \ + --js "${SRC_DIR}/src/ext/utf8.js" \ + --js "${SRC_DIR}/src/ext/sha256.js" \ + --externs "${SRC_DIR}/meta/wme-externs.js" \ + --externs "${SRC_DIR}/meta/jquery-1.9.js" \ + --js_output_file "${TMP_DIR}/gen-${EXT_NAME}-compiled.js" \ + --use_types_for_optimization \ + --warning_level=VERBOSE \ + --jscomp_warning=checkTypes \ + --jscomp_warning=missingProperties \ + --jscomp_off es5Strict \ + --process_jquery_primitives \ + ${EXTRA_PARAMS} + +if [ $? != 0 ]; then + read -p "Stop on error. Press enter to exit..." + exit 1 +fi + +mkdir -p "${DST_DIR}" +cat "${SRC_DIR}/meta/meta-begin.js" \ + "${TMP_DIR}/gen-${EXT_NAME}-compiled.js" \ + "${SRC_DIR}/meta/meta-end.js" \ + > "${DST_DIR}/${EXT_FILE_NAME}.user.js" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is 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. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +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. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + 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 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. Use with the GNU Affero General Public License. + + 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 Affero 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 special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 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 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 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. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + 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 GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3c8c598 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +ABOUT +===== +Validates a map area in Waze Map Editor, highlights issues and generates +a very detailed report with wiki references and solutions. + +WME Validator uses Open Source GPLv3 license, i.e. you may copy, +distribute and modify the software as long as you track changes/dates +in source files. Any modifications to or software including +(via compiler) GPL-licensed code must also be made available under +the GPL along with build & install instructions. + +Please check the doc directory for more information. + +For questions and usage discussions please use official forum: +https://www.waze.com/forum/viewtopic.php?f=819&t=76488 + +Report bugs and issues using GitHub issues tracker: +https://github.com/WMEValidator/validator/issues diff --git a/doc/ChangeLog.txt b/doc/ChangeLog.txt new file mode 100644 index 0000000..976c42c --- /dev/null +++ b/doc/ChangeLog.txt @@ -0,0 +1,731 @@ +30.05.2018: +- Initial public commit + + +03.11.2016 v1.1.20: +- Fixed #23 Unconfirmed road + +04.06.2016 v1.1.19: +- Fixed WME Beta +- Fixed icons in segment properties +- The work is still in progress... + +02.06.2016 v1.1.18: +- Fixed Firefox browser +- Added Validator tab + +01.06.2016 v1.1.17: +- Fixed (some) icons +- Fixed (some) event handlers + +29.01.2016 v1.1.16: +- Fixed Firefox browser (thanks to Glodenox) +- Updated CZ localizations + +13.12.2015 v1.1.15: +- Updated US and CZ localizations + +17.11.2015 v1.1.14: +- Updated styles to match the latest WME +- Updated for PL #171 and #52 +- Updated for US #171 'Incorrect abbreviation' + +16.11.2015 v1.1.13: +- Updated for US #171 'Incorrect abbreviation' +- Added 'At the bottom' option + +11.11.2015 v1.1.11: +- Updated for US #171 'Incorrect abbreviation' +- Minor update: latest WME compatibility +- Removed 'Two-way segments by default' option + +07.05.2015 v1.1.8: +- No changes, sorry :(. + +06.03.2015 v1.1.7: +- Move the development to a new platform + +06.01.2015 v1.1.6: +- Updated for PL #171 'Street name abbreviations' +- Disabled for IL #109 'Too short segment' + +06.01.2015 v1.1.5: +- Temporary disabled #36, 37 'Unneeded node A/B' +- Fixed the crash on a new segment save + +07.11.2014 v1.1.4: +- Maintenance release. No changes :( + +07.10.2014 v1.1.3: +- Updated Polish translations thanks to Zniwek +- Enabled for ES #130-133 'Custom lock levels' +- Updated #114 and #115 'Non-drivable connected to + drivable': excluded Railroads + +01.10.2014 v1.1.2: +- Disabled for IL #28 'Street name on two-way Ramp' +- Updated Polish abbreviations +- Fixed custom checks #135-139 +- Updated Spanish translations thanks to robindlc +- Enabled for NL #150-154 'Lock levels' +- Updated segment properties UI + +30.08.2014 v1.1.1: +- Fixed 'Two-way segments by default' option + +29.08.2014 v1.1.0: +- Compatibility with beta WME + Note: some checks are still disabled +- Updated for BE #160 'Incorrect Freeway name' +- Disabled for IL #91 'Two-way Ramp segment' +- Removed 1 sec startup delay introduced in v0.6.2 + +24.08.2014 v1.0.4: +- Temporary disabled #44, #45 and #50. + Note: some other checks might not work as well. +- Updated Polish translations +- Increased number of custom checks (#130-139) + +04.08.2014 v1.0.3: +- Disabled #200, #201, #300, #301 'Unconfirmed turn' + for dead-ends +- Disabled #77 'Dead-end U-turn' +- Fixed tabs for small fonts +- Other minor bugfixes + +04.07.2014 v1.0.2: +- Complete Hebrew translation thanks to gad_m +- Minor UI changes for right-to-left languages +- Changed keyboard shortcut to 'Alt+V' + +- Added 'for CMs only' note to the Updated by field + +25.06.2014 v1.0.1: +- Disabled for IL #52 'Too long street name' +- Disabled for IL #41, #42 'Reverse connectivity' +- Fixed for IL WME Beta +- Separated Mexico from Spain + +26.05.2014 v1.0.0: +- Re-enabled #150 for AR and CL +- Disabled for IL #115 'Non-drivable connected to drivable' +- Temporarily disabled #74 'Multiple segments at r-about' + (the check will be updated in one of the next releases) +- Updated #52 'Too long street name': 30 letters by default +- Updated #171 for PL: polish abbreviations +- Fixed wiki links for checks #114/115 +- Fixed #36/37 'Unneeded node A/B' for country names +- Fixed leading spaces in custom RegExps +- Fixed: unable to remove debug flag for custom checks +- Added new custom template variables: + '${deadEndA/B}' and '${partialA/B}' +- Google Analytics (I'll share the statistics later) +- Added license + + +- Add May 1st: The segment is locked for you, so Validator did not check it. +- #128 #129 links to forum post, not MDN + + +27.04.2014 v0.9.9: +- Fixed #190 for Cyrillic letters +- Fixed false positive in #171 for FR: 'Rue du Mal Assis' +- Fixed #36 and #37 'Unneeded node' for partial nodes +- Improvements in Settings->About->Available checks +- Temporarily disabled #79 'Too short U-turn connector' + (the check will be updated in one of the next releases) + +- Fixed city checks #190 and #191 + + +15.04.2014 v0.9.8: +- Fixed #24 filtering in 'Reported as' field +- Fixed #173 'No space after abbreviation' for '11.' +- Fixed segment length for newly created segments +- Custom check severities are now warnings by default. + (sorry, I'm a bit busy these days, so a "real" fix will come in one of the next releases) + +- Improved RegExp debug log +- Added a link to custom checks examples on the forum +- Fixed default regexps for #160-163 + +- CHECKS REVIEW: + Added new checks with params to validate city names: + #190 'Lowercase city name' (merged #92) + #191 'Incorrectly abbreviated city name' + #192 'Unneeded spaces in city name' (merged #31) + #193 'No space before/after abbreviation' (merged #33) + +- RegExp .match -> .test + + +08.04.2014 v0.9.7: +- Fixed #170 'Lowercase street name' + +08.04.2014 v0.9.6: +- Fixed #170 'Lowercase street name' +- New for US #106 'No state name selected' +- Disabled skips of locked segment + +07.04.2014 v0.9.5: +- Validator now skips locked segments, so the locks may + be used to hide issues for low-level users. +- New faster 'Show report' method (Chrome only) +- Layers param in permalinks in report + +- Updated Spanish translations thanks to robindlc +- Updated #109 'Too short segment': 5m by default + (6m for BeNeLux) + wiki link added +- Updated #79 'Too short U-turn connector' to report + only same road types +- Updated #24 'Might be incorrect city name' for + Строитель снт vs Строитель 2 снт + +- Configured check parameters are listed now in + 'Settings->About->Available checks' +- New params to override default check description: + 'titleEN', 'problemEN' and 'solutionEN' +- Fixed custom check ${deadEnd} variable for off-screen + nodes +- Fixed custom check ${softTurns} variable + +- CHECKS REVIEW: + All "option1" properties changed to params/regexp + Changed check ID #10 -> #9 + Split check #40 'Soft turns' -> #200 and #300 + 'Node A/B: Unconfirmed turn on minor road' + Split check #86 'Soft turns on primary road' -> #201 and #301 + 'Node A/B: Unconfirmed turn on primary road' + Removed #49 and #111 (Roundabouts) + Added params for #73 'Too short street name' + and #109 'Too short segment' + Added new checks to validate street names: + #160 Freeway name (merged #61 and #80) + #161 Major Highway name (merged #53, #60 and #81) + #162 Minor Highway name (merged #82) + #163 Ramp name (merged #54, #64, #89 and #113) + #164 Primary Street name + #165 Street name + #166 Parking Lot Road name + #167 Railroad name (merged #58) + #169 any type street name (merged #55, #56 and #123) + #170 'Lowercase street name' (merged #93, #94) + #171 'Incorrectly abbreviated street' + (merged #26, #51, #68 and #88) + #172 'Unneeded spaces in street name' (merged #30) + #173 'No space before/after abbreviation' (merged #32) + #174 'Street name spelling mistake' (merged #100) + #175 'Empty street name' (merged #98) + Added new checks to validate street types: + #70 must be a Freeway (merged #83) + #71 must be a Major (merged #62 and #84) + #72 must be a Minor (merged #63 and #85) + +- Reported as: 24 or #24 +- Disabled for RU #77 'Dead-end U-turn' +- Globaly enabled Moscow +- Report TOC links: colons removed +- Show report: direct += operators for reports + + +30.03.2014 v0.9.4: +- Complete French translation of UI and checks. + Many thanks to arbaot and ClementH44! +- New variables for custom checks: + ${altCity[index or delimeter]} + ${altStreet[index or delimeter]} +- Fixed bug with highlighting at the screen edges +- CHECKS REVIEW: + Added 5 new checks #150-#154 for lock levels + Removed: #65, #66, #67, #70, #71, #72, #76, #96, #97, + and #122 + +- Lowered severities for roundabout direction checks + #49 and #111 +- Object.keys -> for ... in + +- Fixed #108 'Node A/B: No connection' and deleted segment +- Globaly enabled Kyiv + + +25.03.2014 v0.9.3: +- Complete Spanish translation of UI and checks. + Languages: Español, Español América Latina and Galego + Many thanks to robindlc and fernandoanguita! +- CHECKS REVIEW: + Removed: #9, #11, #12, #75, #106 (duplicate/unused) + Added params to: + #52 'More than ${n} letters long street name' + #112 'More than ${n} letters long Ramp name' + +- Check parameters are listed now in + 'Settings->About->Available checks' +- Disabled nulls in #116 'Out of range elevations' + +- Added Andorra +- Added a banner to translate Validator into 5 languages + +22.03.2014 v0.9.1: +- Updated #116 'Out of range elevation' for nulls +- New option 'Report external highlights' + +20.03.2014 v0.9.0: +- New user-defined custom checks in Settings->Custom +- New 5 custom checks #130 - #134 for localization +- Validator now generates packages for dependent + countries + +- Fixed non-editable filter option +- Fixed localization packs for Firefox +- Fixed 'Non-drivable connected to drivable' for very + long segments +- Removed 'Toolbox/WMECH: Report highlighted segments' + (now it is on by default) +- Fixed lost TODO labels on upgrade package +- Fixed 'Highlight issues on the map' menu item +- Fixed #57 for other countries (was just enabled for PL) + +- Disabled #106 for PL +- Try-catch for new RegExp parsing option1 + +- WME Validator Localization for LanguageX +- window.WME_Validator_* = {} + ';' + spaces not tabs +- Fixed country name with spaces in Package Wizard +- Generate title/problem/solution for checks which + enabled for dependant countries (see Argentina) + + +14.03.2014 v0.8.3: +- minor fixes +- Updated #106 for PL 'Unneeded name on one-way Ramp' +- Renamed some checks to 'Node A/B: ...' +- Fixed: auto-disable slow checks on zoom levels 0-3 + +09.03.2014 v0.8.2: +- Auto-disable slow checks at zoom levels 0-3 +- Fixed severities for some checks (Unneeded node) + +- Updated #36 and #37 'Unneeded node A/B' for U-turns +- Updated 'Unneeded node A/B' for partial nodes +- NEW for AR: 'Street name matches the RegExp' (calle) + you may use localization pack to set the RegExp for + your country +- Disabled for AR #56 'Incorrect word in street name' + +- HOTFIX 'Too many issues' note in properties (v0.8.1) + +- Removed #75 "BETA: Bow-Ties" + +08.03.2014 v0.8.0: +- Support for localization packages +- Package Wizard: Settings->About->Wizard +- Auto-pause if there are more than 20K of segments +- 'Too many issues' note in segment's properties +- 'Validator is disabled' note in segment's properties + +- NEW for BeNeLux #122 'Incorrect Primary street lock' +- Enabled for AR #56 'Incorrect word in street name' + word 'calle' +- Fixed #38 and #39 'Expired restrictions' +- Updated #98 'Street name with spaces only': + street name with just a dot +- Enabled/disabled few checks for spanish countries +- Updated spanish translations + +- No text direction (match WME) + +28.02.2014 v0.7.4: +- UPD 'Too sharp turn at node A/B': + revCon turns are excluded +- UPD for US 'Lowercase street name (with exceptions)' + now the check ignores [ENSW] at the beginning +- Fixed 'Zoom out to start WME Validator' message +- Fixed Spanish in Swedish language +- UPD howto for 'The segment is overlapping' +- Disabled for AU 'More than 55 letters Ramp name' + +- validate twice again + +24.02.2014 v0.7.3: +- NEW for All 'BETA: Overlapping segments at node A/B' +- NEW for All 'BETA: Too sharp turn at node A/B' +- check IDs moved to the end of the check title + +24.02.2014 v0.7.2: +- NEW for All: 'Obsolete CONST ZN marker' +- UPD 'Closed road': case insensitive +- UPD 'Non-drivable connected to drivable': excluded + short dead-end segments and segments with HNs +- DISABLED for US 'More than 55 letters Ramp name' +- DISABLED for IL 'Less than 3 letters long' +- Fixed 'No connection at node A/B' for false + 'Show report' +- Improved 'Show report' performance +- Fixed toggle on/off +- Few new Spanish translations +- Checks #36, #37, #46, #47, #78, #79, #102 and #103 + are now marked as SLOW +- Fixed few highlight issues (v0.7.1) + +- trL right into checks +- Removed font awesome +- fixed unclickable link in 'Check the forum thread...' + + +19.02.2014 v0.7.0: +- complete Spanish translation thanks to robindlc and + fernandoanguita +- WME language switcher now also switch language of + problem and howtos (but not links) +- 'Updated by' search option for country managers + +- NEW for ALL: 'Out of range elevation' +- NEW for ALL: 'Non-drivable connected to drivable' +- NEW for IT: 'Ramp name starts with an A' (untested) +- NEW for ALL: 'More than 55 letters Ramp' +- UPD #51 for PL 'Incorrectly abbreviated street name' + added: bł., kard., marsz., rtm. +- UPD #101 'Closed road': default marker is '(Closed)' +- UPD #40 'Soft turns on drivable road': check for + both nodes exist +- UPD 'Incorrect lock level' checks for traffic locks +- UPD 'No connection at node A/B': exclude non-editable + segments filter option +- UPD for IL 'Less than 2 letters long street name' +- DISABLED for US 'Two-way Ramp segment' +- What's the problem and howtos are replaced with icons +- fixed WME Color Highlights wiki URLs +- added input placeholders +- now Validator reports max 300 segments per check + +- Font Awesome in the report +- total number of enabled/disabled checks +- checks array -> object +- remove all ck_ constants +- check titles comparison without options + + + +12.02.2014 v0.6.3: +- UPD 'Too short segment': roundabouts are excluded +- ENABLED for Bulgaria #27 'City name on Railroad' +- ENABLED for Chile #59, #86 and #93 +- UPD 'Exclude non-editable segments' option for nodes +- Spanish translations thanks to robindlc and + fernandoanguita +- Spanish set as a default for AR, BO, CL, CR, CO, CU, + DO, EC, GQ, GT, HN, MX, NI, PA, PE, PY, SV, UY, VE +- fixed map scan: not all of the segments were reported +- ISO date in report + +- Color Highlight -> WMECH +- othersegments sometimes does not exist + + +07.02.2014 v0.6.2: +- Validator make a 1 sec pause on startup to allow + other scripts to start first +- Improved startup time +- NEW keyboard shortcut to toggle Validator on/off: + Shift+W +- UPD 'Unneeded node A/B' for segment/turn + restrictions +- ENABLED few checks for Spain +- Spanish localization has started +- FAQ and forum post updated for "flash" +- fixed fallback rules for other countries + + +05.02.2014 v0.6.1: +- NEW search option to include/exclude certain checks: + Reported as (see forum for the details) +- ENABLED for CZ 'Walking Trail instead of a Railroad' +- fixed few bugs with new and deleted segments + +04.02.2014 v0.6.0: +- NEW search tab: your edits, date and city + (see forum for the details) +- NEW for ALL 'SLOW: No connection at node A/B': + a dead-end node is within 5m from another segment +- NEW for ALL 'Too short segment' + (drivable non-dead-end less than 2m long segment) +- NEW for AT, CH, DE 'Incorrect Freeway elevation' +- NEW for ALL right-hand traffic countries + 'Unusual B-A drivable roundabout direction' +- UPD 'Construction zone': TRAVAUX for France, GF, NC, RE +- UPD roundabout checks only for drivable segments now +- ENABLED for AT, CH, DE 'Two-way Freeway segment' + 'Incorrect Freeway lock level' +- DISABLED for (almost) ALL + 'Walking Trail instead of a Railroad' + 'Unneeded name on one-way Ramp' +- UI changes: search for your edits, segments updated since a specific date + or search for a specific city (wildcard are supported, i.e. Greater * Area) +- NEW for ALL 'SLOW: No connection at node A/B': a dead-end node is within 5m from another segment +- NEW for ALL 'Too short segment' (drivable non-dead-end less than 2m long segment) +- NEW for DACH 'Incorrect Freeway elevation' +- NEW for ALL right-hand traffic 'Unusual B-A drivable roundabout direction' +- UPD 'Construction zone': TRAVAUX for France, GF, NC and RE +- UPD 'Two-way drivable roundabout segment' + 'Street name on drivable roundabout' + 'Few outgoing segments at roundabout node A' + 'Few segments at roundabout node A' + 'No connectivity on roundabout' + 'Inconsistent roundabout direction': now only for drivable segments +- ENABLED for DACH 'Two-way Freeway segment', 'Incorrect Freeway lock level' +- DISABLED for (almost) ALL 'Walking Trail instead of a Railroad' +- DISABLED for (almost) ALL: 'Unneeded name on one-way Ramp' +- other minor changes/fixes + +02.02.2014 v0.5.9: +- NEW for ALL: 'Railroad used for comments' +- NEW for ALL: 'Walking Trail used as a Railroad' +- NEW for ALL: 'Unneeded name on one-way Ramp' +- 4xNEW for ALL: 'No inward/outward connectivity at node A/B' (warning) + (DELETED: 'No connectivity at node A/B of drivable road') +- UPD for US 'Lowercase street name' exceptions: to +- UPD for PL 'Incorrect Railroad name': added MPK, SKM and Tramwaje Śląskie +- fixed: in segment properties issues are grouped now, i.e. 'Soft turns (12)' +- fixed: beta editor login +- fixed: Validator enabling/disabling +- polish translations thanks to Zniwek + +31.01.2014 v0.5.8: +- NEW 'Construction zone (only available in the report)' + Default marker: 'CONST ZN' + Marker for Poland: 'REMONT' +- ENABLED for ALL: 'Same endpoints drivable segments' +- fixed: after solving 'No inward connectivity', segment still remains highlighted +- Validator is back in Layer Switcher + Note: you still might experience issues using old permalinks +- other minor changes +- Available checks: english text is grey +- Available checks: no for country +- afterloginchanged -> login LoginManager + +29.01.2014 v0.5.7: +- DISABLED for ALL: 'Same endpoints drivable segments' +- 'Settings->About->Available checks': added translate/localize markers +- Check IDs in the titles + +29.01.2014 v0.5.6: +- new WME beta compatibility +- UPD for Ireland 'Incorrect Highway name' (accents) + +28.01.2014 v0.5.5: +- fixed 'Available checks' crash +- fixed 'Show report' crash (for some countries) + +28.01.2014 v0.5.4: +- NEW for ALL 'U-turn at roundabout entrance' +- NEW for FR, GF, NC, RE 'SLOW: Missing street name accents' +- UPD 'Lowercase street name (with exceptions)': added SK and CZ exceptions +- UPD 'Less than 3 letters long street name': Ramps are excluded +- DISABLED for SK: 'Lowercase street name' +- UPD: 'Street name with a dot': Ramps are excluded +- now you can localize check titles +- some translations for Italy +- Validator is disabled in LayerSwitcher due to auto-enabling on permalink click +- country-specific options (some checks will be merged in the feature versions) + +23.01.2014 v0.5.3: +- NEW for ALL: 'Street name with spaces only' +- UPD 'More than 25 letters street name': for drivables roads only +- few checks enabled/localized for CZ and Slovakia +- 'Settings->About->Available checks' button now shows enabled/disabled checks + for the current (i.e. map top left corner) country + + +22.01.2014 v0.5.2: +- NEW for IT: 'Street name with a dot' +- UPD 'Same endpoints drivable segments': roundabouts are temporarily excluded +- support for RTL languages +- bunch of checks enabled/disabled for Israel +- few checks enabled for Italy +- other minor changes +- styles: move classes down to the elements + + +21.01.2014 v0.5.1: +- NEW for ALL: 'Lowercase city name' +- NEW for US: 'Lowercase street name (with exceptions)' +- NEW for FR, GF, NC, RE 'Lowercase street name' +- NEW for IL, US 'Two-way Ramp segment' +- NEW for IL, US 'Two-way Freeway segment' +- UPD 'Unneeded node A/B': total length is up to 10km +- UPD 'Might be incorrect city name': same names +- UPD 'Might be incorrect city name': De Witt vs Dewitt +- ENABLED for AT, CH, DE 'City name on Freeway' +- setting 'WME: Two-way segments' is off by default +- popup when settings are reset to their defaults +- popup with a changelog +- started adaptations for RTL languages +- started Israel localization +- other changes +- fixed HL issues in WME beta +- destroy features on start scanning +- rewrite dead-end u-turns, so it need only one node + + +20.01.2014 v0.5.0: +- new highlighting system (please report bugs/suggestions) +- all options are reset +- most of the checks are translated into French! +- new check for France: 'Unwanted abbreviation in street name' +- new check for France: '"Vers" in Ramp name' + + +0.4.9: +- fixed 'Two-Way segments by default' for roundabouts + +0.4.8: +- new check: 'More than one outgoing segment connected to roundabout node A' +- now it's a warning: 'More than one segment connected to roundabout node A' +- more French translations +- enabled "City name on named Ramp" for France, GF, NC and RE +- added Reunion +- fixed new segment roundabout error +- new advanced setting: 'Two-Way segments by default' +- a bit adjusted HL twinkeling, major HL update in the next release +- no highlighting for 'Might be incorrect city name' check due to false positives, + but you can still use this check by scanning a map area + +0.4.7: +- FAQ +- begin of French localization! +- new check: 'Soft turns on primary road' for UK and Chile +- 6 new checks for Ireland: "Incorrect Freeway/Major/Minor name" and + "Might be incorrect road type (Freeway/Major/Minor)" +- many HL objects now HLed in groups +- $animatedObjects -> object, not array +- Validator off -> stopAnimation +- Validator off -> HLAllSegments +- disabled 'Incorrect Freeway lock level' for US +- enabled "No lock on Freeway" for US +- fixed "Unneeded node" (state comparison) +- correctly uncount city for might be incomplete city name +- refactoring deleteSeenSegment: much less deletes due to check is segment is + still partial +- fixed: "Might be incorrect city name" (highlighting) +- fixed: "Same endpoints drivable segments" (node != null) + +0.4.6: +- target: Validator for the links +- fixed "Incorrect highway name" for NL +- 4 blinks +- optimized for many highlights +- UK is globally enabled! +- new check: Same endpoints drivable segments +- new check: Too short U-turn connector +- updated "Unneeded node A/B" checks: total length < 1000m & Same endpoints + prevention + +0.4.5: +- fixed "Street name with no space before/after" for "Dr.-Herbert" +- to switch HL just click on the title "Validator" +- link styles +- removed zoom/step + +0.4.4: +- filter option for other drivable roads +- no WCH highlights, just reports +- all filter options are savable +- highlighting turn issues fixed + +0.4.3: +- unneeded node A/B is disabled +- updated reverse roundabout -> Inconsistent roundabout direction +- soft turns report fixed +- new clear report icon +- updated reverse connections +- new default options for Lvl 1, 2, 5 and 6 +- less than 3 letters long report is fixed + +0.4.2: +- check selected before and after the selection if not highlighted +- fixed: uncount deleted segments/cities/reports +- never report new roads, only in New road report? +- No ID streets +- recheck segment if nodes are partial +- adjusted zoom levels +- fixed bug with highlighting mign be incorrected city name +- beta compatibility +- Unneeded node A/B alt streets fixed +- Few Australia reports +- Dead-end U-turns report +- I18n of reports: URLS, problem descriptions and how-tos are country-specific + +WME Map Validator +- report: EXPERIMENTAL: bow-tie intersection -> normal + report overcomplicated intersection + https://www.waze.com/pl/editor/?lon=16.93542&lat=52.39904&zoom=7&layers=TBFTFFTTTTTTTTTTTTTFTTTTTT&env=row +- report 2 segments in one node on a roundabout +- report: dot at the end for US Mountail Ln. +- add belgium and luxemburg +- add singapur and brunei +- updated Toolbox Colors +- new option: Highlight reported issues on the map +- check segment center and start extent +- total number of segments in summary and % of errors +- WCH and Toolbox are on by default +- store silent mode in localstore +- limit of segments per report +- BBreport: warning 50000 chars +- show the settings at the beginning of the report? +- sort city/steeet/segment +- in report major/critical permalinks -> orange and red +- count city names and use for the report anme with the biggest counter +- show total number of issues at the end of the report +- report button color depend on severity of the issue: red, orange and blue +- new filter: exclude minor issues +- list of available checks +- continue scan on error +- scan the map continuesly, i.e. every move end +- runtime reports (save username, road rank, editable flag) +- UI change filters on a first page, all reports by default +- new access lists + cache +- road type rank + + +v0.3.2: + - new option: Exclude non-drivable and streets (default for Lvl 5 and 6) + - new option: Exclude highway segments (default for Lvl 1 and 2) + - options rethinking +New connection reports: + - Unterminated segments + - Unneeded junctions + - Unknown segment direction + + - Unlock all segments not only for Poland + +v0.3.1 RC2 @ 26.11.2013: +New set of reports (connection issues): + - Drivable roads are not connected + - Expired segment/turn restrictions + - Soft turns + - Reverse connections + - Unconnected segments + - Self-connected segments + - Unconnected one-way segments + - No U-turn on long (150 m) dead-end segments + - Two-way roundabout segments + - Unconnected roundabout segments + - Reverse connected (B->A) roundabout segments + +New set of reports for Netherland: + - Incorrect Freeway/Highway street names + - Incorrect Nnum road types + - Ramps start with a number +New set of fixes for Netherland: + - Lock Freeway/Highway + - Correct 'ri.' abreviation + +UI changes: +- separate BB button shows report in BBcode for forum/PM posts + +Bugs & Issues: +- few issues with Toolbox/WCH interaction were fixed +- Run once mode permalinks were fixed + +Other changes +- report of a very long street names has moved to a Polish country pack diff --git a/doc/RELENG.md b/doc/RELENG.md new file mode 100644 index 0000000..8f27760 --- /dev/null +++ b/doc/RELENG.md @@ -0,0 +1,18 @@ +WME Validator Release Process +============================= + + **MAKE SURE ALL THE FILES ARE IN UNICODE** + + 0. Check all window.console lines and comment them out. + - Check TEST:, TODO: and BETA: + + 1. Check header: + - country locks + - levels + - release and expiration dates + 2. Compile: + - release.sh + + 3. Test basic functionality by scanning and panning around + + 4. Test in Tampermonkey and Firefox diff --git a/i18n b/i18n new file mode 160000 index 0000000..48503d6 --- /dev/null +++ b/i18n @@ -0,0 +1 @@ +Subproject commit 48503d6c1c95ae2827092935ee638683d42d4944 diff --git a/meta/I18n-begin.js b/meta/I18n-begin.js new file mode 100644 index 0000000..20abe91 --- /dev/null +++ b/meta/I18n-begin.js @@ -0,0 +1,5 @@ +/************************************************************************* + * Built-In Localizations + *************************************************************************/ + +var _translations = { diff --git a/meta/I18n-end.js b/meta/I18n-end.js new file mode 100644 index 0000000..4443437 --- /dev/null +++ b/meta/I18n-end.js @@ -0,0 +1 @@ +}; // Built-In Localizations diff --git a/meta/jquery-1.9.js b/meta/jquery-1.9.js new file mode 100644 index 0000000..2b63768 --- /dev/null +++ b/meta/jquery-1.9.js @@ -0,0 +1,2161 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Externs for jQuery 1.9.1 + * + * Note that some functions use different return types depending on the number + * of parameters passed in. In these cases, you may need to annotate the type + * of the result in your code, so the JSCompiler understands which type you're + * expecting. For example: + * var elt = /** @type {Element} * / (foo.get(0)); + * + * @see http://api.jquery.com/ + * @externs + */ + +/** + * @typedef {(Window|Document|Element|Array.|string|jQuery| + * NodeList)} + */ +var jQuerySelector; + +/** @typedef {function(...)|Array.} */ +var jQueryCallback; + +/** @typedef { + { + accepts: (Object.|undefined), + async: (?boolean|undefined), + beforeSend: (function(jQuery.jqXHR, (jQueryAjaxSettings|Object.))|undefined), + cache: (?boolean|undefined), + complete: (function(jQuery.jqXHR, string)|undefined), + contents: (Object.|undefined), + contentType: (?string|undefined), + context: (Object.|jQueryAjaxSettings|undefined), + converters: (Object.|undefined), + crossDomain: (?boolean|undefined), + data: (Object.|?string|Array.|undefined), + dataFilter: (function(string, string):?|undefined), + dataType: (?string|undefined), + error: (function(jQuery.jqXHR, string, string)|undefined), + global: (?boolean|undefined), + headers: (Object.|undefined), + ifModified: (?boolean|undefined), + isLocal: (?boolean|undefined), + jsonp: (?string|undefined), + jsonpCallback: (?string|function()|undefined), + mimeType: (?string|undefined), + password: (?string|undefined), + processData: (?boolean|undefined), + scriptCharset: (?string|undefined), + statusCode: (Object.|undefined), + success: (function(?, string, jQuery.jqXHR)|undefined), + timeout: (?number|undefined), + traditional: (?boolean|undefined), + type: (?string|undefined), + url: (?string|undefined), + username: (?string|undefined), + xhr: (function():(ActiveXObject|XMLHttpRequest)|undefined), + xhrFields: (Object.|undefined) + }} */ +var jQueryAjaxSettings; + +/** + * @constructor + * @param {(jQuerySelector|Element|Object|Array.|jQuery|string| + * function())=} arg1 + * @param {(Element|jQuery|Document| + * Object.)=} arg2 + * @return {!jQuery} + */ +function jQuery(arg1, arg2) {} + +/** + * @constructor + * @extends {jQuery} + * @param {(jQuerySelector|Element|Object|Array.|jQuery|string| + * function())=} arg1 + * @param {(Element|jQuery|Document| + * Object.)=} arg2 + * @return {!jQuery} + */ +function $(arg1, arg2) {} + +/** + * @param {(jQuerySelector|Array.|string|jQuery)} arg1 + * @param {Element=} context + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.add = function(arg1, context) {}; + +/** + * @param {(jQuerySelector|Array.|string|jQuery)=} arg1 + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.addBack = function(arg1) {}; + +/** + * @param {(string|function(number,String))} arg1 + * @return {!jQuery} + */ +jQuery.prototype.addClass = function(arg1) {}; + +/** + * @param {(string|Element|jQuery|function(number))} arg1 + * @param {(string|Element|Array.|jQuery)=} content + * @return {!jQuery} + */ +jQuery.prototype.after = function(arg1, content) {}; + +/** + * @param {(string|jQueryAjaxSettings|Object.)} arg1 + * @param {(jQueryAjaxSettings|Object.)=} settings + * @return {jQuery.jqXHR} + */ +jQuery.ajax = function(arg1, settings) {}; + +/** + * @param {(string|jQueryAjaxSettings|Object.)} arg1 + * @param {(jQueryAjaxSettings|Object.)=} settings + * @return {jQuery.jqXHR} + */ +$.ajax = function(arg1, settings) {}; + +/** + * @param {function(!jQuery.event,XMLHttpRequest,(jQueryAjaxSettings|Object.))} handler + * @return {!jQuery} + */ +jQuery.prototype.ajaxComplete = function(handler) {}; + +/** + * @param {function(!jQuery.event,jQuery.jqXHR,(jQueryAjaxSettings|Object.),*)} handler + * @return {!jQuery} + */ +jQuery.prototype.ajaxError = function(handler) {}; + +/** + * @param {(string|function((jQueryAjaxSettings|Object.),(jQueryAjaxSettings|Object.),jQuery.jqXHR))} dataTypes + * @param {function((jQueryAjaxSettings|Object.),(jQueryAjaxSettings|Object.),jQuery.jqXHR)=} handler + */ +jQuery.ajaxPrefilter = function(dataTypes, handler) {}; + +/** + * @param {(string|function((jQueryAjaxSettings|Object.),(jQueryAjaxSettings|Object.),jQuery.jqXHR))} dataTypes + * @param {function((jQueryAjaxSettings|Object.),(jQueryAjaxSettings|Object.),jQuery.jqXHR)=} handler + */ +$.ajaxPrefilter = function(dataTypes, handler) {}; + +/** + * @param {function(!jQuery.event,jQuery.jqXHR,(jQueryAjaxSettings|Object.))} handler + * @return {!jQuery} + */ +jQuery.prototype.ajaxSend = function(handler) {}; + +/** @const {jQueryAjaxSettings|Object.} */ +jQuery.ajaxSettings; + +/** @const {jQueryAjaxSettings|Object.} */ +$.ajaxSettings = {}; + +/** @type {Object.} */ +jQuery.ajaxSettings.flatOptions = {}; + +/** @type {Object.} */ +$.ajaxSettings.flatOptions = {}; + +/** @type {boolean} */ +jQuery.ajaxSettings.processData; + +/** @type {boolean} */ +$.ajaxSettings.processData; + +/** @type {Object.} */ +jQuery.ajaxSettings.responseFields = {}; + +/** @type {Object.} */ +$.ajaxSettings.responseFields = {}; + +/** @param {jQueryAjaxSettings|Object.} options */ +jQuery.ajaxSetup = function(options) {}; + +/** @param {jQueryAjaxSettings|Object.} options */ +$.ajaxSetup = function(options) {}; + +/** + * @param {function()} handler + * @return {!jQuery} + */ +jQuery.prototype.ajaxStart = function(handler) {}; + +/** + * @param {function()} handler + * @return {!jQuery} + */ +jQuery.prototype.ajaxStop = function(handler) {}; + +/** + * @param {function(!jQuery.event,XMLHttpRequest,(jQueryAjaxSettings|Object.), ?)} handler + * @return {!jQuery} + */ +jQuery.prototype.ajaxSuccess = function(handler) {}; + +/** + * @deprecated Please use .addBack(selector) instead. + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.andSelf = function() {}; + +/** + * @param {Object.} properties + * @param {(string|number|function()|Object.)=} arg2 + * @param {(string|function())=} easing + * @param {function()=} complete + * @return {!jQuery} + */ +jQuery.prototype.animate = function(properties, arg2, easing, complete) {}; + +/** + * @param {(string|Element|Array.|jQuery|function(number,string))} arg1 + * @param {...(string|Element|Array.|jQuery)} content + * @return {!jQuery} + */ +jQuery.prototype.append = function(arg1, content) {}; + +/** + * @param {(jQuerySelector|Element|jQuery)} target + * @return {!jQuery} + */ +jQuery.prototype.appendTo = function(target) {}; + +/** + * @param {(string|Object.)} arg1 + * @param {(string|number|boolean|function(number,string))=} arg2 + * @return {(string|!jQuery)} + */ +jQuery.prototype.attr = function(arg1, arg2) {}; + +/** + * @param {(string|Element|jQuery|function())} arg1 + * @param {(string|Element|Array.|jQuery)=} content + * @return {!jQuery} + */ +jQuery.prototype.before = function(arg1, content) {}; + +/** + * @param {(string|Object.)} arg1 + * @param {(Object.|function(!jQuery.event=)|boolean)=} eventData + * @param {(function(!jQuery.event=)|boolean)=} arg3 + * @return {!jQuery} + */ +jQuery.prototype.bind = function(arg1, eventData, arg3) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.blur = function(arg1, handler) {}; + +/** + * @constructor + * @private + */ +jQuery.callbacks = function () {}; + +/** + * @param {string=} flags + * @return {jQuery.callbacks} + */ +jQuery.Callbacks = function (flags) {}; + +/** @param {function()} callbacks */ +jQuery.callbacks.prototype.add = function(callbacks) {}; + +/** @return {undefined} */ +jQuery.callbacks.prototype.disable = function() {}; + +/** @return {undefined} */ +jQuery.callbacks.prototype.empty = function() {}; + +/** @param {...*} var_args */ +jQuery.callbacks.prototype.fire = function(var_args) {}; + +/** @return {boolean} */ +jQuery.callbacks.prototype.fired = function() {}; + +/** @param {...*} var_args */ +jQuery.callbacks.prototype.fireWith = function(var_args) {}; + +/** + * @param {function()} callback + * @return {boolean} + * @nosideeffects + */ +jQuery.callbacks.prototype.has = function(callback) {}; + +/** @return {undefined} */ +jQuery.callbacks.prototype.lock = function() {}; + +/** @return {boolean} */ +jQuery.callbacks.prototype.locked = function() {}; + +/** @param {function()} callbacks */ +jQuery.callbacks.prototype.remove = function(callbacks) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.change = function(arg1, handler) {}; + +/** + * @param {jQuerySelector=} selector + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.children = function(selector) {}; + +/** + * @param {string=} queueName + * @return {!jQuery} + */ +jQuery.prototype.clearQueue = function(queueName) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.click = function(arg1, handler) {}; + +/** + * @param {boolean=} withDataAndEvents + * @param {boolean=} deepWithDataAndEvents + * @return {!jQuery} + * @suppress {checkTypes} see https://code.google.com/p/closure-compiler/issues/detail?id=583 + */ +jQuery.prototype.clone = function(withDataAndEvents, deepWithDataAndEvents) {}; + +/** + * @param {(jQuerySelector|jQuery|Element|string)} arg1 + * @param {Element=} context + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.closest = function(arg1, context) {}; + +/** + * @param {Element} container + * @param {Element} contained + * @return {boolean} + */ +jQuery.contains = function(container, contained) {}; + +/** + * @param {Element} container + * @param {Element} contained + * @return {boolean} + */ +$.contains = function(container, contained) {}; + +/** + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.contents = function() {}; + +/** @type {Element|Document} */ +jQuery.prototype.context; + +/** + * @param {(string|Object.)} arg1 + * @param {(string|number|function(number,*))=} arg2 + * @return {(string|!jQuery)} + */ +jQuery.prototype.css = function(arg1, arg2) {}; + +/** @type {Object.} */ +jQuery.cssHooks; + +/** @type {Object.} */ +$.cssHooks; + +/** + * @param {Element} elem + * @param {string=} key + * @param {*=} value + * @return {*} + */ +jQuery.data = function(elem, key, value) {}; + +/** + * @param {(string|Object.)=} arg1 + * @param {*=} value + * @return {*} + */ +jQuery.prototype.data = function(arg1, value) {}; + +/** + * @param {Element} elem + * @param {string=} key + * @param {*=} value + * @return {*} + */ +$.data = function(elem, key, value) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.dblclick = function(arg1, handler) {}; + +/** + * @constructor + * @implements {jQuery.Promise} + * @param {function()=} opt_fn + * @see http://api.jquery.com/category/deferred-object/ + */ +jQuery.deferred = function(opt_fn) {}; + +/** + * @constructor + * @extends {jQuery.deferred} + * @param {function()=} opt_fn + * @return {jQuery.Deferred} + */ +jQuery.Deferred = function(opt_fn) {}; + +/** + * @constructor + * @extends {jQuery.deferred} + * @param {function()=} opt_fn + * @see http://api.jquery.com/category/deferred-object/ + */ +$.deferred = function(opt_fn) {}; + +/** + * @constructor + * @extends {jQuery.deferred} + * @param {function()=} opt_fn + * @return {jQuery.deferred} + */ +$.Deferred = function(opt_fn) {}; + +/** + * @override + * @param {jQueryCallback} alwaysCallbacks + * @param {jQueryCallback=} alwaysCallbacks2 + * @return {jQuery.deferred} + */ +jQuery.deferred.prototype.always + = function(alwaysCallbacks, alwaysCallbacks2) {}; + +/** + * @override + * @param {jQueryCallback} doneCallbacks + * @param {jQueryCallback=} doneCallbacks2 + * @return {jQuery.deferred} + */ +jQuery.deferred.prototype.done = function(doneCallbacks, doneCallbacks2) {}; + +/** + * @override + * @param {jQueryCallback} failCallbacks + * @param {jQueryCallback=} failCallbacks2 + * @return {jQuery.deferred} + */ +jQuery.deferred.prototype.fail = function(failCallbacks, failCallbacks2) {}; + +/** + * @param {...*} var_args + * @return {jQuery.deferred} + */ +jQuery.deferred.prototype.notify = function(var_args) {}; + +/** + * @param {Object} context + * @param {...*} var_args + * @return {jQuery.deferred} + */ +jQuery.deferred.prototype.notifyWith = function(context, var_args) {}; + +/** + * @deprecated Please use deferred.then() instead. + * @override + * @param {function()=} doneFilter + * @param {function()=} failFilter + * @param {function()=} progressFilter + * @return {jQuery.Promise} + */ +jQuery.deferred.prototype.pipe = + function(doneFilter, failFilter, progressFilter) {}; + +/** + * @param {jQueryCallback} progressCallbacks + * @return {jQuery.deferred} + */ +jQuery.deferred.prototype.progress = function(progressCallbacks) {}; + +/** + * @param {Object=} target + * @return {jQuery.Promise} + */ +jQuery.deferred.prototype.promise = function(target) {}; + +/** + * @param {...*} var_args + * @return {jQuery.deferred} + */ +jQuery.deferred.prototype.reject = function(var_args) {}; + +/** + * @param {Object} context + * @param {Array.<*>=} args + * @return {jQuery.deferred} + */ +jQuery.deferred.prototype.rejectWith = function(context, args) {}; + +/** + * @param {...*} var_args + * @return {jQuery.deferred} + */ +jQuery.deferred.prototype.resolve = function(var_args) {}; + +/** + * @param {Object} context + * @param {Array.<*>=} args + * @return {jQuery.deferred} + */ +jQuery.deferred.prototype.resolveWith = function(context, args) {}; + +/** @return {string} */ +jQuery.deferred.prototype.state = function() {}; + +/** + * @override + * @param {jQueryCallback} doneCallbacks + * @param {jQueryCallback=} failCallbacks + * @param {jQueryCallback=} progressCallbacks + * @return {jQuery.deferred} + */ +jQuery.deferred.prototype.then + = function(doneCallbacks, failCallbacks, progressCallbacks) {}; + +/** + * @param {number} duration + * @param {string=} queueName + * @return {!jQuery} + */ +jQuery.prototype.delay = function(duration, queueName) {}; + +/** + * @param {string} selector + * @param {(string|Object.)} arg2 + * @param {(function(!jQuery.event=)|Object.)=} arg3 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.delegate = function(selector, arg2, arg3, handler) {}; + +/** + * @param {Element} elem + * @param {string=} queueName + */ +jQuery.dequeue = function(elem, queueName) {}; + +/** + * @param {string=} queueName + * @return {!jQuery} + */ +jQuery.prototype.dequeue = function(queueName) {}; + +/** + * @param {Element} elem + * @param {string=} queueName + */ +$.dequeue = function(elem, queueName) {}; + +/** + * @param {jQuerySelector=} selector + * @return {!jQuery} + */ +jQuery.prototype.detach = function(selector) {}; + +/** + * @param {Object} collection + * @param {function((number|string),?)} callback + * @return {Object} + */ +jQuery.each = function(collection, callback) {}; + +/** + * @param {function(number,Element)} fnc + * @return {!jQuery} + */ +jQuery.prototype.each = function(fnc) {}; + +/** + * @param {Object} collection + * @param {function((number|string),?)} callback + * @return {Object} + */ +$.each = function(collection, callback) {}; + +/** @return {!jQuery} */ +jQuery.prototype.empty = function() {}; + +/** + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.end = function() {}; + +/** + * @param {number} arg1 + * @return {!jQuery} + */ +jQuery.prototype.eq = function(arg1) {}; + +/** @param {string} message */ +jQuery.error = function(message) {}; + +/** + * @deprecated Please use .on( "error", handler ) instead. + * @param {(function(!jQuery.event=)|Object.)} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.error = function(arg1, handler) {}; + +/** @param {string} message */ +$.error = function(message) {}; + +/** + * @constructor + * @param {string} eventType + */ +jQuery.event = function(eventType) {}; + +/** + * @constructor + * @extends {jQuery.event} + * @param {string} eventType + * @param {Object=} properties + * @return {jQuery.Event} + */ +jQuery.Event = function(eventType, properties) {}; + +/** + * @constructor + * @extends {jQuery.event} + * @param {string} eventType + */ +$.event = function(eventType) {}; + +/** + * @constructor + * @extends {jQuery.event} + * @param {string} eventType + * @param {Object=} properties + * @return {$.Event} + */ +$.Event = function(eventType, properties) {}; + +/** @type {Element} */ +jQuery.event.prototype.currentTarget; + +/** @type {Object.} */ +jQuery.event.prototype.data; + +/** @type {Element} */ +jQuery.event.prototype.delegateTarget; + +/** + * @return {boolean} + * @nosideeffects + */ +jQuery.event.prototype.isDefaultPrevented = function() {}; + +/** + * @return {boolean} + * @nosideeffects + */ +jQuery.event.prototype.isImmediatePropagationStopped = function() {}; + +/** + * @return {boolean} + * @nosideeffects + */ +jQuery.event.prototype.isPropagationStopped = function() {}; + +/** @type {string} */ +jQuery.event.prototype.namespace; + +/** @type {Event} */ +jQuery.event.prototype.originalEvent; + +/** @type {number} */ +jQuery.event.prototype.pageX; + +/** @type {number} */ +jQuery.event.prototype.pageY; + +/** @return {undefined} */ +jQuery.event.prototype.preventDefault = function() {}; + +/** @type {Object.} */ +jQuery.event.prototype.props; + +/** @type {Element} */ +jQuery.event.prototype.relatedTarget; + +/** @type {*} */ +jQuery.event.prototype.result; + +/** @return {undefined} */ +jQuery.event.prototype.stopImmediatePropagation = function() {}; + +/** @return {undefined} */ +jQuery.event.prototype.stopPropagation = function() {}; + +/** @type {Element} */ +jQuery.event.prototype.target; + +/** @type {number} */ +jQuery.event.prototype.timeStamp; + +/** @type {string} */ +jQuery.event.prototype.type; + +/** @type {number} */ +jQuery.event.prototype.which; + +/** + * @param {(Object|boolean)} arg1 + * @param {...*} var_args + * @return {Object} + */ +jQuery.extend = function(arg1, var_args) {}; + +/** + * @param {(Object|boolean)} arg1 + * @param {...*} var_args + * @return {Object} + */ +jQuery.prototype.extend = function(arg1, var_args) {}; + +/** + * @param {(Object|boolean)} arg1 + * @param {...*} var_args + * @return {Object} + */ +$.extend = function(arg1, var_args) {}; + +/** + * @param {(string|number|function())=} duration + * @param {(function()|string)=} arg2 + * @param {function()=} callback + * @return {!jQuery} + */ +jQuery.prototype.fadeIn = function(duration, arg2, callback) {}; + +/** + * @param {(string|number|function())=} duration + * @param {(function()|string)=} arg2 + * @param {function()=} callback + * @return {!jQuery} + */ +jQuery.prototype.fadeOut = function(duration, arg2, callback) {}; + +/** + * @param {(string|number)} duration + * @param {number} opacity + * @param {(function()|string)=} arg3 + * @param {function()=} callback + * @return {!jQuery} + */ +jQuery.prototype.fadeTo = function(duration, opacity, arg3, callback) {}; + +/** + * @param {(string|number|function())=} duration + * @param {(string|function())=} easing + * @param {function()=} callback + * @return {!jQuery} + */ +jQuery.prototype.fadeToggle = function(duration, easing, callback) {}; + +/** + * @param {(jQuerySelector|function(number,Element)|Element|jQuery)} arg1 + * @return {!jQuery} + * @see http://api.jquery.com/filter/ + */ +jQuery.prototype.filter = function(arg1) {}; + +/** + * @param {(jQuerySelector|jQuery|Element)} arg1 + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.find = function(arg1) {}; + +/** @return {!jQuery} */ +jQuery.prototype.first = function() {}; + +/** @see http://docs.jquery.com/Plugins/Authoring */ +jQuery.fn = jQuery.prototype; + +/** @see http://docs.jquery.com/Plugins/Authoring */ +$.fn = $.prototype; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.focus = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.focusin = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.focusout = function(arg1, handler) {}; + +/** @const */ +jQuery.fx = {}; + +/** @const */ +$.fx = {}; + +/** @type {number} */ +jQuery.fx.interval; + +/** @type {number} */ +$.fx.interval; + +/** @type {boolean} */ +jQuery.fx.off; + +/** @type {boolean} */ +$.fx.off; + +/** + * @param {string} url + * @param {(Object.|string| + * function(string,string,jQuery.jqXHR))=} data + * @param {(function(string,string,jQuery.jqXHR)|string)=} success + * @param {string=} dataType + * @return {jQuery.jqXHR} + */ +jQuery.get = function(url, data, success, dataType) {}; + +/** + * @param {number=} index + * @return {(Element|Array.)} + * @nosideeffects + */ +jQuery.prototype.get = function(index) {}; + +/** + * @param {string} url + * @param {(Object.|string| + * function(string,string,jQuery.jqXHR))=} data + * @param {(function(string,string,jQuery.jqXHR)|string)=} success + * @param {string=} dataType + * @return {jQuery.jqXHR} + */ +$.get = function(url, data, success, dataType) {}; + +/** + * @param {string} url + * @param {(Object.| + * function(Object.,string,jQuery.jqXHR))=} data + * @param {function(Object.,string,jQuery.jqXHR)=} success + * @return {jQuery.jqXHR} + * @see http://api.jquery.com/jquery.getjson/#jQuery-getJSON-url-data-success + */ +jQuery.getJSON = function(url, data, success) {}; + +/** + * @param {string} url + * @param {(Object.| + * function(Object.,string,jQuery.jqXHR))=} data + * @param {function(Object.,string,jQuery.jqXHR)=} success + * @return {jQuery.jqXHR} + * @see http://api.jquery.com/jquery.getjson/#jQuery-getJSON-url-data-success + */ +$.getJSON = function(url, data, success) {}; + +/** + * @param {string} url + * @param {function(Node,string,jQuery.jqXHR)=} success + * @return {jQuery.jqXHR} + */ +jQuery.getScript = function(url, success) {}; + +/** + * @param {string} url + * @param {function(Node,string,jQuery.jqXHR)=} success + * @return {jQuery.jqXHR} + */ +$.getScript = function(url, success) {}; + +/** @param {string} code */ +jQuery.globalEval = function(code) {}; + +/** @param {string} code */ +$.globalEval = function(code) {}; + +/** + * @param {Array.<*>} arr + * @param {function(*,number)} fnc + * @param {boolean=} invert + * @return {Array.<*>} + */ +jQuery.grep = function(arr, fnc, invert) {}; + +/** + * @param {Array.<*>} arr + * @param {function(*,number)} fnc + * @param {boolean=} invert + * @return {Array.<*>} + */ +$.grep = function(arr, fnc, invert) {}; + +/** + * @param {(string|Element)} arg1 + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.has = function(arg1) {}; + +/** + * @param {string} className + * @return {boolean} + * @nosideeffects + */ +jQuery.prototype.hasClass = function(className) {}; + +/** + * @param {Element} elem + * @return {boolean} + * @nosideeffects + */ +jQuery.hasData = function(elem) {}; + +/** + * @param {Element} elem + * @return {boolean} + * @nosideeffects + */ +$.hasData = function(elem) {}; + +/** + * @param {(string|number|function(number,number))=} arg1 + * @return {(number|!jQuery)} + */ +jQuery.prototype.height = function(arg1) {}; + +/** + * @param {(string|number|function())=} duration + * @param {(function()|string)=} arg2 + * @param {function()=} callback + * @return {!jQuery} + */ +jQuery.prototype.hide = function(duration, arg2, callback) {}; + +/** @param {boolean} hold */ +jQuery.holdReady = function(hold) {}; + +/** @param {boolean} hold */ +$.holdReady = function(hold) {}; + +/** + * @param {function(!jQuery.event=)} arg1 + * @param {function(!jQuery.event=)=} handlerOut + * @return {!jQuery} + */ +jQuery.prototype.hover = function(arg1, handlerOut) {}; + +/** + * @param {(string|function(number,string))=} arg1 + * @return {(string|!jQuery)} + */ +jQuery.prototype.html = function(arg1) {}; + +/** + * @param {*} value + * @param {Array.<*>} arr + * @param {number=} fromIndex + * @return {number} + * @nosideeffects + */ +jQuery.inArray = function(value, arr, fromIndex) {}; + +/** + * @param {*} value + * @param {Array.<*>} arr + * @param {number=} fromIndex + * @return {number} + * @nosideeffects + */ +$.inArray = function(value, arr, fromIndex) {}; + +/** + * @param {(jQuerySelector|Element|jQuery)=} arg1 + * @return {number} + */ +jQuery.prototype.index = function(arg1) {}; + +/** + * @return {number} + * @nosideeffects + */ +jQuery.prototype.innerHeight = function() {}; + +/** + * @return {number} + * @nosideeffects + */ +jQuery.prototype.innerWidth = function() {}; + +/** + * @param {(jQuerySelector|Element|jQuery)} target + * @return {!jQuery} + */ +jQuery.prototype.insertAfter = function(target) {}; + +/** + * @param {(jQuerySelector|Element|jQuery)} target + * @return {!jQuery} + */ +jQuery.prototype.insertBefore = function(target) {}; + +/** + * @param {(jQuerySelector|function(number)|jQuery|Element)} arg1 + * @return {boolean} + */ +jQuery.prototype.is = function(arg1) {}; + +/** + * @param {*} obj + * @return {boolean} + * @nosideeffects + */ +jQuery.isArray = function(obj) {}; + +/** + * @param {*} obj + * @return {boolean} + * @nosideeffects + */ +$.isArray = function(obj) {}; + +/** + * @param {Object} obj + * @return {boolean} + * @nosideeffects + */ +jQuery.isEmptyObject = function(obj) {}; + +/** + * @param {Object} obj + * @return {boolean} + * @nosideeffects + */ +$.isEmptyObject = function(obj) {}; + +/** + * @param {*} obj + * @return {boolean} + * @nosideeffects + */ +jQuery.isFunction = function(obj) {}; + +/** + * @param {*} obj + * @return {boolean} + * @nosideeffects + */ +$.isFunction = function(obj) {}; + +/** + * @param {*} value + * @return {boolean} + * @nosideeffects + */ +jQuery.isNumeric = function(value) {}; + +/** + * @param {*} value + * @return {boolean} + * @nosideeffects + */ +$.isNumeric = function(value) {}; + +/** + * @param {*} obj + * @return {boolean} + * @nosideeffects + */ +jQuery.isPlainObject = function(obj) {}; + +/** + * @param {*} obj + * @return {boolean} + * @nosideeffects + */ +$.isPlainObject = function(obj) {}; + +/** + * @param {*} obj + * @return {boolean} + * @nosideeffects + */ +jQuery.isWindow = function(obj) {}; + +/** + * @param {*} obj + * @return {boolean} + * @nosideeffects + */ +$.isWindow = function(obj) {}; + +/** + * @param {Element} node + * @return {boolean} + * @nosideeffects + */ +jQuery.isXMLDoc = function(node) {}; + +/** + * @param {Element} node + * @return {boolean} + * @nosideeffects + */ +$.isXMLDoc = function(node) {}; + +/** @type {string} */ +jQuery.prototype.jquery; + +/** + * @constructor + * @extends {XMLHttpRequest} + * @implements {jQuery.Promise} + * @private + * @see http://api.jquery.com/jQuery.ajax/#jqXHR + */ +jQuery.jqXHR = function () {}; + +/** + * @override + * @param {jQueryCallback} alwaysCallbacks + * @param {jQueryCallback=} alwaysCallbacks2 + * @return {jQuery.jqXHR} + */ +jQuery.jqXHR.prototype.always = + function(alwaysCallbacks, alwaysCallbacks2) {}; + +/** + * @deprecated + * @param {function()} callback + * @return {jQuery.jqXHR} +*/ +jQuery.jqXHR.prototype.complete = function (callback) {}; + +/** + * @override + * @param {jQueryCallback} doneCallbacks + * @return {jQuery.jqXHR} + */ +jQuery.jqXHR.prototype.done = function(doneCallbacks) {}; + +/** + * @deprecated + * @param {function()} callback + * @return {jQuery.jqXHR} +*/ +jQuery.jqXHR.prototype.error = function (callback) {}; + +/** + * @override + * @param {jQueryCallback} failCallbacks + * @return {jQuery.jqXHR} + */ +jQuery.jqXHR.prototype.fail = function(failCallbacks) {}; + +/** + * @deprecated + * @override + */ +jQuery.jqXHR.prototype.onreadystatechange = function (callback) {}; + +/** + * @override + * @param {function()=} doneFilter + * @param {function()=} failFilter + * @param {function()=} progressFilter + * @return {jQuery.jqXHR} + */ +jQuery.jqXHR.prototype.pipe = + function(doneFilter, failFilter, progressFilter) {}; + +/** + * @deprecated + * @param {function()} callback + * @return {jQuery.jqXHR} +*/ +jQuery.jqXHR.prototype.success = function (callback) {}; + +/** + * @override + * @param {jQueryCallback} doneCallbacks + * @param {jQueryCallback=} failCallbacks + * @param {jQueryCallback=} progressCallbacks + * @return {jQuery.jqXHR} + */ +jQuery.jqXHR.prototype.then = + function(doneCallbacks, failCallbacks, progressCallbacks) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.keydown = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.keypress = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.keyup = function(arg1, handler) {}; + +/** @return {!jQuery} */ +jQuery.prototype.last = function() {}; + +/** @type {number} */ +jQuery.prototype.length; + +/** + * @deprecated Please avoid the document loading Event invocation of + * .load() and use .on( "load", handler ) instead. (The AJAX + * module invocation signature is OK.) + * @param {(function(!jQuery.event=)|Object.|string)} arg1 + * @param {(function(!jQuery.event=)|Object.|string)=} arg2 + * @param {function(string,string,XMLHttpRequest)=} complete + * @return {!jQuery} + */ +jQuery.prototype.load = function(arg1, arg2, complete) {}; + +/** + * @param {*} obj + * @return {Array.<*>} + */ +jQuery.makeArray = function(obj) {}; + +/** + * @param {*} obj + * @return {Array.<*>} + */ +$.makeArray = function(obj) {}; + +/** + * @param {(Array.<*>|Object.)} arg1 + * @param {(function(*,number)|function(*,(string|number)))} callback + * @return {Array.<*>} + */ +jQuery.map = function(arg1, callback) {}; + +/** + * @param {function(number,Element)} callback + * @return {!jQuery} + */ +jQuery.prototype.map = function(callback) {}; + +/** + * @param {(Array.<*>|Object.)} arg1 + * @param {(function(*,number)|function(*,(string|number)))} callback + * @return {Array.<*>} + */ +$.map = function(arg1, callback) {}; + +/** + * @param {Array.<*>} first + * @param {Array.<*>} second + * @return {Array.<*>} + */ +jQuery.merge = function(first, second) {}; + +/** + * @param {Array.<*>} first + * @param {Array.<*>} second + * @return {Array.<*>} + */ +$.merge = function(first, second) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.mousedown = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.mouseenter = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.mouseleave = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.mousemove = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.mouseout = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.mouseover = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.mouseup = function(arg1, handler) {}; + +/** + * @param {jQuerySelector=} selector + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.next = function(selector) {}; + +/** + * @param {string=} selector + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.nextAll = function(selector) {}; + +/** + * @param {(jQuerySelector|Element)=} arg1 + * @param {jQuerySelector=} filter + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.nextUntil = function(arg1, filter) {}; + +/** + * @param {boolean=} removeAll + * @return {Object} + */ +jQuery.noConflict = function(removeAll) {}; + +/** + * @param {boolean=} removeAll + * @return {Object} + */ +$.noConflict = function(removeAll) {}; + +/** + * @return {function()} + * @nosideeffects + */ +jQuery.noop = function() {}; + +/** + * @return {function()} + * @nosideeffects + */ +$.noop = function() {}; + +/** + * @param {(jQuerySelector|Array.|function(number)|jQuery)} arg1 + * @return {!jQuery} + */ +jQuery.prototype.not = function(arg1) {}; + +/** + * @return {number} + * @nosideeffects + */ +jQuery.now = function() {}; + +/** + * @return {number} + * @nosideeffects + */ +$.now = function() {}; + +/** + * @param {(string|Object.)=} arg1 + * @param {(string|function(!jQuery.event=))=} selector + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.off = function(arg1, selector, handler) {}; + +/** + * @param {({left:number,top:number}| + * function(number,{top:number,left:number}))=} arg1 + * @return {({left:number,top:number}|!jQuery)} + */ +jQuery.prototype.offset = function(arg1) {}; + +/** + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.offsetParent = function() {}; + +/** + * @param {(string|Object.)} arg1 + * @param {*=} selector + * @param {*=} data + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.on = function(arg1, selector, data, handler) {}; + +/** + * @param {(string|Object.)} arg1 + * @param {*=} arg2 + * @param {*=} arg3 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.one = function(arg1, arg2, arg3, handler) {}; + +/** + * @param {boolean=} includeMargin + * @return {number} + * @nosideeffects + */ +jQuery.prototype.outerHeight = function(includeMargin) {}; + +/** + * @param {boolean=} includeMargin + * @return {number} + * @nosideeffects + */ +jQuery.prototype.outerWidth = function(includeMargin) {}; + +/** + * @param {(Object.|Array.>)} obj + * @param {boolean=} traditional + * @return {string} + */ +jQuery.param = function(obj, traditional) {}; + +/** + * @param {(Object.|Array.>)} obj + * @param {boolean=} traditional + * @return {string} + */ +$.param = function(obj, traditional) {}; + +/** + * @param {jQuerySelector=} selector + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.parent = function(selector) {}; + +/** + * @param {jQuerySelector=} selector + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.parents = function(selector) {}; + +/** + * @param {(jQuerySelector|Element)=} arg1 + * @param {jQuerySelector=} filter + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.parentsUntil = function(arg1, filter) {}; + +/** + * @param {string} data + * @param {(Element|boolean)=} context + * @param {boolean=} keepScripts + * @return {Array.} + */ +jQuery.parseHTML = function(data, context, keepScripts) {}; + +/** + * @param {string} data + * @param {(Element|boolean)=} context + * @param {boolean=} keepScripts + * @return {Array.} + */ +$.parseHTML = function(data, context, keepScripts) {}; + +/** + * @param {string} json + * @return {string|number|Object.|Array.|boolean} + */ +jQuery.parseJSON = function(json) {}; + +/** + * @param {string} json + * @return {Object.} + */ +$.parseJSON = function(json) {}; + +/** + * @param {string} data + * @return {Document} + */ +jQuery.parseXML = function(data) {}; + +/** + * @param {string} data + * @return {Document} + */ +$.parseXML = function(data) {}; + +/** + * @return {{left:number,top:number}} + * @nosideeffects + */ +jQuery.prototype.position = function() {}; + +/** + * @param {string} url + * @param {(Object.|string| + * function(string,string,jQuery.jqXHR))=} data + * @param {(function(string,string,jQuery.jqXHR)|string|null)=} success + * @param {string=} dataType + * @return {jQuery.jqXHR} + */ +jQuery.post = function(url, data, success, dataType) {}; + +/** + * @param {string} url + * @param {(Object.|string| + * function(string,string,jQuery.jqXHR))=} data + * @param {(function(string,string,jQuery.jqXHR)|string|null)=} success + * @param {string=} dataType + * @return {jQuery.jqXHR} + */ +$.post = function(url, data, success, dataType) {}; + +/** + * @param {(string|Element|jQuery|function(number,string))} arg1 + * @param {(string|Element|jQuery)=} content + * @return {!jQuery} + */ +jQuery.prototype.prepend = function(arg1, content) {}; + +/** + * @param {(jQuerySelector|Element|jQuery)} target + * @return {!jQuery} + */ +jQuery.prototype.prependTo = function(target) {}; + +/** + * @param {jQuerySelector=} selector + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.prev = function(selector) {}; + +/** + * @param {jQuerySelector=} selector + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.prevAll = function(selector) {}; + +/** + * @param {(jQuerySelector|Element)=} arg1 + * @param {jQuerySelector=} filter + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.prevUntil = function(arg1, filter) {}; + +/** + * @param {(string|Object)=} type + * @param {Object=} target + * @return {jQuery.Promise} + */ +jQuery.prototype.promise = function(type, target) {}; + +/** + * @interface + * @private + * @see http://api.jquery.com/Types/#Promise + */ +jQuery.Promise = function () {}; + +/** + * @param {jQueryCallback} alwaysCallbacks + * @param {jQueryCallback=} alwaysCallbacks2 + * @return {jQuery.Promise} + */ +jQuery.Promise.prototype.always = + function(alwaysCallbacks, alwaysCallbacks2) {}; + +/** + * @param {jQueryCallback} doneCallbacks + * @return {jQuery.Promise} + */ +jQuery.Promise.prototype.done = function(doneCallbacks) {}; + +/** + * @param {jQueryCallback} failCallbacks + * @return {jQuery.Promise} + */ +jQuery.Promise.prototype.fail = function(failCallbacks) {}; + +/** + * @param {function()=} doneFilter + * @param {function()=} failFilter + * @param {function()=} progressFilter + * @return {jQuery.Promise} + */ +jQuery.Promise.prototype.pipe = + function(doneFilter, failFilter, progressFilter) {}; + +/** + * @param {jQueryCallback} doneCallbacks + * @param {jQueryCallback=} failCallbacks + * @param {jQueryCallback=} progressCallbacks + * @return {jQuery.Promise} + */ +jQuery.Promise.prototype.then = + function(doneCallbacks, failCallbacks, progressCallbacks) {}; + +/** + * @param {(string|Object.)} arg1 + * @param {(string|number|boolean|function(number,String))=} arg2 + * @return {(string|boolean|!jQuery)} + */ +jQuery.prototype.prop = function(arg1, arg2) {}; + +/** + * @param {...*} var_args + * @return {function()} + */ +jQuery.proxy = function(var_args) {}; + +/** + * @param {...*} var_args + * @return {function()} + */ +$.proxy = function(var_args) {}; + +/** + * @param {Array.} elements + * @param {string=} name + * @param {Array.<*>=} args + * @return {!jQuery} + */ +jQuery.prototype.pushStack = function(elements, name, args) {}; + +/** + * @param {(string|Array.|function(function()))=} queueName + * @param {(Array.|function(function()))=} arg2 + * @return {(Array.|!jQuery)} + */ +jQuery.prototype.queue = function(queueName, arg2) {}; + +/** + * @param {Element} elem + * @param {string=} queueName + * @param {(Array.|function())=} arg3 + * @return {(Array.|!jQuery)} + */ +jQuery.queue = function(elem, queueName, arg3) {}; + +/** + * @param {Element} elem + * @param {string=} queueName + * @param {(Array.|function())=} arg3 + * @return {(Array.|!jQuery)} + */ +$.queue = function(elem, queueName, arg3) {}; + +/** + * @param {function()} handler + * @return {!jQuery} + */ +jQuery.prototype.ready = function(handler) {}; + +/** + * @param {string=} selector + * @return {!jQuery} + */ +jQuery.prototype.remove = function(selector) {}; + +/** + * @param {string} attributeName + * @return {!jQuery} + */ +jQuery.prototype.removeAttr = function(attributeName) {}; + +/** + * @param {(string|function(number,string))=} arg1 + * @return {!jQuery} + */ +jQuery.prototype.removeClass = function(arg1) {}; + +/** + * @param {(string|Array.)=} arg1 + * @return {!jQuery} + */ +jQuery.prototype.removeData = function(arg1) {}; + +/** + * @param {Element} elem + * @param {string=} name + * @return {!jQuery} + */ +jQuery.removeData = function(elem, name) {}; + +/** + * @param {Element} elem + * @param {string=} name + * @return {!jQuery} + */ +$.removeData = function(elem, name) {}; + +/** + * @param {string} propertyName + * @return {!jQuery} + */ +jQuery.prototype.removeProp = function(propertyName) {}; + +/** + * @param {jQuerySelector} target + * @return {!jQuery} + */ +jQuery.prototype.replaceAll = function(target) {}; + +/** + * @param {(string|Element|jQuery|function())} arg1 + * @return {!jQuery} + */ +jQuery.prototype.replaceWith = function(arg1) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.resize = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.scroll = function(arg1, handler) {}; + +/** + * @param {number=} value + * @return {(number|!jQuery)} + */ +jQuery.prototype.scrollLeft = function(value) {}; + +/** + * @param {number=} value + * @return {(number|!jQuery)} + */ +jQuery.prototype.scrollTop = function(value) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.select = function(arg1, handler) {}; + +/** + * @return {string} + * @nosideeffects + */ +jQuery.prototype.serialize = function() {}; + +/** + * @return {Array.>} + * @nosideeffects + */ +jQuery.prototype.serializeArray = function() {}; + +/** + * @param {(string|number|function())=} duration + * @param {(function()|string)=} arg2 + * @param {function()=} callback + * @return {!jQuery} + */ +jQuery.prototype.show = function(duration, arg2, callback) {}; + +/** + * @param {jQuerySelector=} selector + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.siblings = function(selector) {}; + +/** + * @deprecated Please use the .length property instead. + * @return {number} + * @nosideeffects + */ +jQuery.prototype.size = function() {}; + +/** + * @param {number} start + * @param {number=} end + * @return {!jQuery} + */ +jQuery.prototype.slice = function(start, end) {}; + +/** + * @param {(Object.|string|number)=} optionsOrDuration + * @param {(function()|string)=} completeOrEasing + * @param {function()=} complete + * @return {!jQuery} + */ +jQuery.prototype.slideDown = + function(optionsOrDuration, completeOrEasing, complete) {}; + +/** + * @param {(Object.|string|number)=} optionsOrDuration + * @param {(function()|string)=} completeOrEasing + * @param {function()=} complete + * @return {!jQuery} + */ +jQuery.prototype.slideToggle = + function(optionsOrDuration, completeOrEasing, complete) {}; + +/** + * @param {(Object.|string|number)=} optionsOrDuration + * @param {(function()|string)=} completeOrEasing + * @param {function()=} complete + * @return {!jQuery} + */ +jQuery.prototype.slideUp = + function(optionsOrDuration, completeOrEasing, complete) {}; + +/** + * @param {(boolean|string)=} arg1 + * @param {boolean=} arg2 + * @param {boolean=} jumpToEnd + * @return {!jQuery} + */ +jQuery.prototype.stop = function(arg1, arg2, jumpToEnd) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.submit = function(arg1, handler) {}; + +/** @type {Object.} + * @deprecated Please try to use feature detection instead. + */ +jQuery.support; + +/** @type {Object.} + * @deprecated Please try to use feature detection instead. + */ +$.support; + +/** + * @deprecated Please try to use feature detection instead. + * @type {boolean} + */ +jQuery.support.boxModel; + +/** + * @deprecated Please try to use feature detection instead. + * @type {boolean} + */ +$.support.boxModel; + +/** @type {boolean} */ +jQuery.support.changeBubbles; + +/** @type {boolean} */ +$.support.changeBubbles; + +/** @type {boolean} */ +jQuery.support.cors; + +/** @type {boolean} */ +$.support.cors; + +/** @type {boolean} */ +jQuery.support.cssFloat; + +/** @type {boolean} */ +$.support.cssFloat; + +/** @type {boolean} */ +jQuery.support.hrefNormalized; + +/** @type {boolean} */ +$.support.hrefNormalized; + +/** @type {boolean} */ +jQuery.support.htmlSerialize; + +/** @type {boolean} */ +$.support.htmlSerialize; + +/** @type {boolean} */ +jQuery.support.leadingWhitespace; + +/** @type {boolean} */ +$.support.leadingWhitespace; + +/** @type {boolean} */ +jQuery.support.noCloneEvent; + +/** @type {boolean} */ +$.support.noCloneEvent; + +/** @type {boolean} */ +jQuery.support.opacity; + +/** @type {boolean} */ +$.support.opacity; + +/** @type {boolean} */ +jQuery.support.style; + +/** @type {boolean} */ +$.support.style; + +/** @type {boolean} */ +jQuery.support.submitBubbles; + +/** @type {boolean} */ +$.support.submitBubbles; + +/** @type {boolean} */ +jQuery.support.tbody; + +/** @type {boolean} */ +$.support.tbody; + +/** + * @param {(string|number|boolean|function(number,string))=} arg1 + * @return {(string|!jQuery)} + */ +jQuery.prototype.text = function(arg1) {}; + +/** + * @return {Array.} + * @nosideeffects + */ +jQuery.prototype.toArray = function() {}; + +/** + * Refers to the method from the Effects category. There used to be a toggle + * method on the Events category which was removed starting version 1.9. + * @param {(number|string|Object.|boolean)=} arg1 + * @param {(function()|string)=} arg2 + * @param {function()=} arg3 + * @return {!jQuery} + */ +jQuery.prototype.toggle = function(arg1, arg2, arg3) {}; + +/** + * @param {(string|boolean|function(number,string,boolean))=} arg1 + * @param {boolean=} flag + * @return {!jQuery} + */ +jQuery.prototype.toggleClass = function(arg1, flag) {}; + +/** + * @param {(string|jQuery.event)} arg1 + * @param {...*} var_args + * @return {!jQuery} + */ +jQuery.prototype.trigger = function(arg1, var_args) {}; + +/** + * @param {string|jQuery.event} eventType + * @param {Array.<*>=} extraParameters + * @return {*} + */ +jQuery.prototype.triggerHandler = function(eventType, extraParameters) {}; + +/** + * @param {string} str + * @return {string} + * @nosideeffects + */ +jQuery.trim = function(str) {}; + +/** + * @param {string} str + * @return {string} + * @nosideeffects + */ +$.trim = function(str) {}; + +/** + * @param {*} obj + * @return {string} + * @nosideeffects + */ +jQuery.type = function(obj) {}; + +/** + * @param {*} obj + * @return {string} + * @nosideeffects + */ +$.type = function(obj) {}; + +/** + * @param {(string|function(!jQuery.event=)|jQuery.event)=} arg1 + * @param {(function(!jQuery.event=)|boolean)=} arg2 + * @return {!jQuery} + */ +jQuery.prototype.unbind = function(arg1, arg2) {}; + +/** + * @param {string=} arg1 + * @param {(string|Object.)=} arg2 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.undelegate = function(arg1, arg2, handler) {}; + +/** + * @param {Array.} arr + * @return {Array.} + */ +jQuery.unique = function(arr) {}; + +/** + * @param {Array.} arr + * @return {Array.} + */ +$.unique = function(arr) {}; + +/** + * @deprecated Please use .on( "unload", handler ) instead. + * @param {(function(!jQuery.event=)|Object.)} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.unload = function(arg1, handler) {}; + +/** @return {!jQuery} */ +jQuery.prototype.unwrap = function() {}; + +/** + * @param {(string|Array.|function(number,*))=} arg1 + * @return {(string|number|Array.|!jQuery)} + */ +jQuery.prototype.val = function(arg1) {}; + +/** + * Note: The official documentation (https://api.jquery.com/jQuery.when/) says + * jQuery.when accepts deferreds, but it actually accepts any type, e.g.: + * + * jQuery.when(jQuery.ready, jQuery.ajax(''), jQuery('#my-element'), 1) + * + * If an argument is not an "observable" (a promise-like object) it is wrapped + * into a promise. + * @param {*} deferred + * @param {...*} deferreds + * @return {jQuery.Promise} + */ +jQuery.when = function(deferred, deferreds) {}; + +/** + * Note: See jQuery.when(). + * @param {*} deferred + * @param {...*} deferreds + * @return {jQuery.Promise} + */ +$.when = function(deferred, deferreds) {}; + +/** + * @param {(string|number|function(number,number))=} arg1 + * @return {(number|!jQuery)} + */ +jQuery.prototype.width = function(arg1) {}; + +/** + * @param {(string|jQuerySelector|Element|jQuery|function(number))} arg1 + * @return {!jQuery} + */ +jQuery.prototype.wrap = function(arg1) {}; + +/** + * @param {(string|jQuerySelector|Element|jQuery)} wrappingElement + * @return {!jQuery} + */ +jQuery.prototype.wrapAll = function(wrappingElement) {}; + +/** + * @param {(string|jQuerySelector|Element|jQuery|function(number))} arg1 + * @return {!jQuery} + */ +jQuery.prototype.wrapInner = function(arg1) {}; + diff --git a/meta/meta-begin.js b/meta/meta-begin.js new file mode 100644 index 0000000..208d0c1 --- /dev/null +++ b/meta/meta-begin.js @@ -0,0 +1,31 @@ +// ==UserScript== +// @name WME Validator +// @version 1.1.20 +// @description This script validates a map area in Waze Map Editor, highlights issues and generates a very detailed report with wiki references and solutions +// @match https://editor-beta.waze.com/*editor/* +// @match https://www.waze.com/*editor/* +// @exclude https://www.waze.com/*user/*editor/* +// @grant none +// @icon http://s3.amazonaws.com/uso_ss/icon/191016/large.png?1388868317 +// @namespace a +// @author berestovskyy +// @copyright 2013-2018 berestovskyy +// ==/UserScript== +// +/* + * LICENSE: + * This script has some features locked for certain + * countries/editor levels, so there are 2 restrictions: + * + * 1. You may not modify or reverse engineer the script. + * 2. You may not use the script for commercial purposes. + * + * The script is distributed 'as is'. No warranty of any + * kind is expressed or implied. You use at your own risk. + * + * If you do not agree with the terms of this license, + * you must remove the script files from your storage + * devices and cease to use WME Validator. + * + */ +(function () { \ No newline at end of file diff --git a/meta/meta-end.js b/meta/meta-end.js new file mode 100644 index 0000000..2d9c82a --- /dev/null +++ b/meta/meta-end.js @@ -0,0 +1 @@ +})() \ No newline at end of file diff --git a/meta/wme-externs.js b/meta/wme-externs.js new file mode 100644 index 0000000..9559c71 --- /dev/null +++ b/meta/wme-externs.js @@ -0,0 +1,393 @@ +/* + * wme-externs.js -- WME extern definitions for closure compiler + * Copyright (C) 2013-2018 Andriy Berestovskyy + * + * This file is part of WME Validator: https://github.com/WMEValidator/ + * + * WME Validator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * WME Validator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with WME Validator. If not, see . + */ + +/////////////////////////////////////////////////////////////////////////// +// I18n Namespace +var I18n = { + locale: "", + translations: { + en: { + layers: { + name: {}, + }, + }, + }, +} + +/////////////////////////////////////////////////////////////////////////// +// OpenLayers Namespace +/** @type {Object} */ +var OpenLayers = { + options: { + displayInLayerSwitcher: true, + renderers: [], + uniqueName: "", + shortcutKey: "", + accelerator: "", + units: "", + styleMap: {}, + projection: {}, + rendererOptions: { zIndexing: true }, + + graphicZIndex: 0, + fillOpacity: 0, + strokeOpacity: 0, + strokeWidth: 0, + pointRadius: 0, + }, + Bounds: { + left: 0, + right: 0, + top: 0, + bottom: 0, + containsBounds: function (bounds, partial, inclusive) { }, + getWidth: function () { }, + getHeight: function () { }, + getCenterLonLat: function () { }, + }, + Class: 0, + Feature: { + Vector: function (a, b) { }, + }, + Geometry: { + Point: function (a, b) { }, + }, + Style: function (a, b) { }, + Layer: { + }, + Projection: function (a) { }, +}; + +/** @constructor */ +OpenLayers.Layer.Vector = function (a, b) { + this.destroyFeatures = function () { }; + this.addFeatures = function (a) { }; + this.setOpacity = function (a) { }; + this.setVisibility = function (a) { }; + this.visibility = true; + this.unrenderedFeatures = {}; +} +/** + * @constructor + * @param {Object=} b + */ +OpenLayers.StyleMap = function (a, b) { + this.addUniqueValueRules = function (a, b, c) { }; +} +/** @constructor */ +OpenLayers.LonLat = function (a, b) { + this.lon = 0; + this.lat = 0; + this.equals = function (e) { }; + this.containsLonLat = function (e) { }; + this.transform = function (s, d) { }; +}; + +/////////////////////////////////////////////////////////////////////////// +// Waze Namespace +var Waze = { + Action: { + CreateRoundabout: {}, + AddAlternateStreet: function (a, b) { }, + UpdateSegmentAddress: function (a, b) { }, + UpdateObject: function (a, b) { } + }, + Config: { + segments: { + zoomToRoadType: {}, + }, + }, + Control: { + Permalink: { + layerVisibilityBitmask: {} + }, + }, + Feature: { + Vector: { + Segment: { + DEFAULT_NEW_SEGMENT_ATTRIBUTES: { + revDirection: false + } + } + } + }, + Renderer: { + ExtendedSVG: {}, + }, + Model: { + ObjectType: { + SEGMENT: "", + } + }, + Util: { + localStorage: { + get: function (a) { }, + set: function (a, b) { } + } + } +}; +/** @constructor */ +Waze.NODE = function () { + /** @type {Object} */ + this.bounds; + this.areConnectionsEditable; + this.getAngleToSegment; + this.geometry = { + id: "", + /** @type {Object} */ + bounds: {}, + distanceTo: function (l, o) { }, + getGeodesicLength: function (p) { }, + }; +} +/** @constructor */ +Waze.ACTION = function () { + this.roundaboutSegments = []; + this.doSubAction = function (a) { }; +} +/** @constructor */ +Waze.SEGMENT = function () { + this.getID = function () { }; + this.atPoint = function (c, tx, ty) { }; + this.id = ""; + this.fid = 0; + this.state = ""; + this.selected = false; + this.layer = {}; + this.arePropertiesEditable = function () { }; + /** @type {Object} */ + this.bounds; + this.geometry = { + id: "", + /** @type {Object} */ + bounds: {}, + distanceTo: function (l, o) { }, + getGeodesicLength: function (p) { }, + }, + this.getAddress = function () { }; + this.getDirection = function () { }; + this.isTollRoad = function () { }; + + this.attributes = { + fromNodeID: 0, + toNodeID: 0, + junctionID: 0, + hasHNs: 0, + primaryStreetID: 0, + createdBy: 0, + createdOn: 0, + streetIDs: [], + level: 0, + lockRank: 0, + rank: 0, + roadType: 0, + revCrossSpeed: 0, + fwdCrossSpeed: 0, + updatedBy: 0, + updatedOn: 0, + fwdRestrictions: [], + revRestrictions: [], + fwdTurnsLocked: true, + revTurnsLocked: true, + }; +} +/** @constructor */ +Waze.RESTRICTION = function () { + this.allDay = true; + this.days = 0; + this.description = ""; + this.isInThePast = function () { }; + this.enabled = true; + this.fromDate = ""; + this.fromTime = ""; + this.toDate = ""; + this.toTime = ""; + this.vehicleTypes = -1; +}; + +var CITY = { + getID: function () { }, + attributes: { + countryID: 0, + stateID: 0, + isEmpty: true, + name: "" + } +} + +var STREET = { + getID: function () { }, + cityID: 0, + isEmpty: true, + name: "" +} + +var ADDRESS = { + street: {}, + city: {}, + state: { name: "" }, + country: { name: "" }, +} + +var ATTRIBUTES = { + // node + connections: {}, + restrictions: {}, + segIDs: [], + partial: true +} + +var Components = { interfaces: { gmIGreasemonkeyService: 0 } }; +String.prototype.startsWith = function (a) { }; + +/////////////////////////////////////////////////////////////////////////// +// W Namespace +var W = { + accelerators: { + events: { + register: function (a, b, c) { }, + }, + }, + MapLayers: { + layerVisibilityBitmask: {} + }, + map: { + mapState: { + getLayerVisibilityBitmask: function () { }, + _getLayerVisibilityBitmask: function () { }, + }, + addLayer: function (a) { }, + dragging: false, + projection: {}, + displayProjection: {}, + layers: { + uniqueName: "", + displayInLayerSwitcher: 0, + setVisibility: function (a) { }, + getVisibility: function () { } + }, + getCenter: function () { }, + getControlsByClass: function (c) { }, + getExtent: function () { }, + getZoom: function () { }, + getLayersByName: function (a) { }, + panTo: function (center) { }, + raiseLayer: function (a, b) { }, + zoomTo: function (zoom) { }, + zoomToExtent: function (ext) { } + }, + loginManager: { + events: { on: 0, un: 0, register: 0 }, + user: { + id: 0, + userName: 0, + normalizedLevel: 0, + editableCountryIDs: [], + isStaffUser: function () { }, + isCountryManager: function () { } + }, + expired: 0, + previousUser: 0, + getLoggedInUser: function () { }, + initialize: function () { }, + isLoggedIn: function () { }, + getUserRank: function () { }, + login: function (e, t) { }, + logout: function () { }, + changeUserName: function (e, t) { } + }, // loginManager + selectionManager: { + events: { on: 0, un: 0 }, + selectedItems: [], + }, // selectionManager + model: { + isLeftHand: false, + events: { on: 0, un: 0 }, + actionManager: { + events: { on: 0, un: 0 }, + unsavedActionsNum: function () { } + }, + segments: { + getObjectArray: function () { }, + objects: {}, + topCityID: 0 + }, + nodes: { + get: function (i) { } + }, + streets: { + get: function (i) { } + }, + cities: { + get: function (i) { } + }, + states: { + get: function (i) { }, + getByAttributes: function (i) { } + }, + countries: { + get: function (i) { }, + getByAttributes: function (i) { } + }, + users: { + get: function (i) { } + } + }, + controller: {}, +}; + +/////////////////////////////////////////////////////////////////////////// +// window Namespace +window.OpenLayers = {}; +window.$ = function (e) { }; +window.Waze = {}; +window.W = {}; +window.viewHelpers = { + formatRank: function (r) { } +}; +window.require = {}; + +/////////////////////////////////////////////////////////////////////////// +// Other externs + +Event.layer = { + id: "", + visibility: false, +}; + +Error.prepareStackTrace = function (a, b) { }; +//var CallSite = { +// getLineNumber: function(){} +//} + +var WME_Validator_I18n = {}; +window.WME_Validator_I18n = {}; + +window.AudioContext = { + createOscillator: function () { }, + destination: null +}; + +window.webkitAudioContext = { +}; + +window.OscillatorNode = { + connect: function (dest) { } +}; diff --git a/src/basic.js b/src/basic.js new file mode 100644 index 0000000..7ccb49b --- /dev/null +++ b/src/basic.js @@ -0,0 +1,809 @@ +/* + * basic.js -- WME Validator basic functions and handlers + * Copyright (C) 2013-2018 Andriy Berestovskyy + * + * This file is part of WME Validator: https://github.com/WMEValidator/ + * + * WME Validator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * WME Validator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with WME Validator. If not, see . + */ + +/************************************************************************* + * BASIC FUNCTIONS + *************************************************************************/ + +/** + * Escape message string + * @param {string} msg + */ +function esc(msg) { + return msg.split("\"").join("\\\"").split("\n").join("\\n"); +} + +/** + * Escape regular expression + * @param {string} e + */ +function escRE(e) { + return e + .split("\\").join("\\\\") + .split("^").join("\\^") + .split("$").join("\\$") + .split("+").join("\\+") + .split("?").join("\\?") + .split(":").join("\\:") + .split("!").join("\\!") + .split(".").join("\\.") + .split("-").join("\\-") + .split("*").join(".*") + .split("(").join("\\(") + .split(")").join("\\)") + .split("[").join("\\[") + .split("]").join("\\]") + .split("{").join("\\{") + .split("}").join("\\}") + ; +} + +/** + * Get a message + * @param {string} mType + * @param {string} msg + * @param {boolean=} newLine + */ +function getMsg(mType, msg, newLine) { + return WV_NAME + " v" + WV_VERSION + + (mType ? " " + mType : "") + + (msg ? ":" + + (newLine ? "\n" : " ") + + msg : ""); +} + +/** + * Log a message + * @param {string} msg + */ +function log(msg) { + // CFCALLTHIS1(CF_LOG, CFGET(CF_CONSOLE), getMsg("", msg)); + async(alog(getMsg("", msg))); +} + +/** + * Show error message + * @param {string} msg + */ +function error(msg) { + var s = getMsg(CFGET(CF_SERROR), msg, true); + async(alog(s)); + if (!isErrorFlag()) { + // set error flag + setErrorFlag(); + CFCALL1(CF_ALERT, s); + } + + // pause scanning + async(F_PAUSE); + + // error code + return -1; +} + +/** + * Show warning message + * @param {string} msg + */ +function warning(msg) { + var s = getMsg(CFGET(CF_SWARN), msg, true); + async(alog(s)); + CFCALL1(CF_ALERT, s); + + // error code + return -1; +} + +/** + * Show info message + * @param {string} msg + */ +function info(msg) { + var s = getMsg("", msg, true); + CFCALL1(CF_ALERT, s); + + // error code + return -1; +} + +/** + * Command pattern: decrypt and execute a function + * @param {number} id + * @param {...*} args + */ +function dde(id, args) { + var efunc = _WV.$functions[id]; + + if (CFABSENT(CF_WARN)) + // critical functions in DDE + CFADD(CFGET(CF_CONSOLE)[CFGET(CF_SWARN)], alert); + + switch (CFCALL1(CF_CLASSCODE, efunc)) { + case CC_STRING: + // try to decrypt the function + _WV.$functions[id] = efunc = CFEVAL1(CF_EVAL, + CFCALL2(CF_TEADECRYPT, efunc, "" + id)); + case CC_FUNCTION: + return efunc.apply( + _WV, [].slice.call(arguments, 1) + ); + } + return error("" + id); +} + +/** + * Execute the function synchronously + * @param {*=} param + */ +function sync(func, param) { + switch (CFCALL1(CF_CLASSCODE, func)) { + case CC_NUMBER: + return CFCALL2(CF_DDE, func, param); + case CC_STRING: + return CFEVAL1(CF_EVAL, func); + case CC_FUNCTION: + return func(param); + } +} + +/** + * Execute the function asynchronously + * @param {*=} param + * @param {number=} inter + */ +function async(func, param, inter) { + var i = 0; + if (inter) i = inter; + function asyncEval() { CFEVAL1(CF_EVAL, func); } + function asyncE() { + if (CFABSENT(CF_LOG)) { + // critical functions in ASYNC after timeout + var c = CFGET(CF_WINDOW)[CFGET(CF_SCONSOLE)]; + CFADD(c, c[CFGET(CF_SLOG)], c[CFGET(CF_SERROR)]); + } + CFCALL2(CF_DDE, func, param); + } + + function callTime(what) { + if (CFABSENT(CF_JSONPARSE)) + // critical functions in ASYNC + CFADD(JSON.parse, dde); + CFCALLTHIS2(CF_SETTIMEOUT, CFGET(CF_WINDOW), what, i) + } + + switch (CFCALL1(CF_CLASSCODE, func)) { + case CC_NUMBER: + callTime(asyncE); + break; + case CC_STRING: + callTime(asyncEval); + break; + case CC_FUNCTION: + callTime(func); + break; + } +} + +/** + * Clear Watch Dogs + */ +function clearWD() { + // kill WGs + CFCALLTHIS1(CF_CLEARTIMEOUT, CFGET(CF_WINDOW), _RT.$WDmoveID); + CFCALLTHIS1(CF_CLEARTIMEOUT, CFGET(CF_WINDOW), _RT.$WDloadID); + _RT.$WDmoveID = -1; + _RT.$WDloadID = -1; +} + +/** + * Update timer + */ +function updateTimer(nstate) { + var csec = Date.now() / 1e3; + + // update timer + if (RTStateIs(ST_RUN)) + _RT.$timer.$secInRun += csec - _RT.$timer.$lastUpdate; + if (RTStateIs(ST_STOP)) + _RT.$timer.$secInRun = 0; + _RT.$timer.$lastUpdate = csec; +} + +/** + * Set run time state + */ +function setRTState(nstate) { + // you cannot pause on stop + if (RTStateIs(ST_STOP) && ST_PAUSE === nstate) + nstate = ST_STOP; + + updateTimer(nstate); + + // set state + _RT.$state = nstate; + + // update UI + async(F_UPDATEUI); +} + +/** + * Clear report + */ +function clearReport() { + _RT.$seen = {}; + _RT.$revalidate = {}; + _REP = { + // debug counter + $debugCounter: LIMIT_DEBUG, + // limit per check has exceeded + $isLimitPerCheck: false, + // at least one editable segment found + $isEditableFound: false, + $counterTotal: 0, + $maxSeverity: 0, + $incompleteIDs: {}, + $users: {}, + $reportCounters: {}, + $cityCounters: {}, + $countries: {}, + $cities: {}, + $streets: {}, + $cityIDs: {}, + $unsortedCityIDs: [], + $sortedCityIDs: [], + }; + + _RT.$reportEditableNotFound = false; +} + +/** + * Beep if not muted + * @param {string=} oscType + */ +function beep(dur, oscType) { + try { + if (_UI.pSettings.pScanner.oSounds.CHECKED) + _AUDIO.beep(dur, oscType); + } + catch (e) { } +} + +/** + * Set error flag + */ +function setErrorFlag() { _RT.$error = true } + +/** + * Get error flag + */ +function isErrorFlag() { return _RT.$error } +/** + * Clear error flag + */ +function clearErrorFlag() { _RT.$error = false } + +/** + * Check run time state + */ +function RTStateIs(st) { return getRTState() === st } +/** + * Returns current run time state + */ +function getRTState() { return _RT.$state } + +/** + * Log a message anonymously + * @param {string} msg + */ +function alog(msg) { + async(function () { + var de = CFCALLTHIS1(CF_CREATEELEMENT, CFGET(CF_DOCUMENT), "i"); + de[CFGET(CF_SSETATTRIBUTE)](CFGET(CF_SONCLICK), + CFGET(CF_SCONSOLE) + "." + + CFGET(CF_SLOG) + "(\"" + esc(msg) + "\")"); + de[CFGET(CF_SONCLICK)](null); + }); +} + +/** + * Highlight segments + */ +function HLAllSegments() { + if (RTStateIs(ST_STOP) || RTStateIs(ST_PAUSE)) { + if (_UI.pSettings.pScanner.oHLReported.CHECKED) + sync(F_VALIDATE, false); + else + sync(F_VALIDATE, true); + } + // always update UI to update zoom levels/allowed countries + async(F_UPDATEUI); +} + +/** + * Force Highlight segments + */ +function ForceHLAllSegments() { + _RT.$isMapChanged = true; + HLAllSegments(); +} + +/** + * Reset Defaults + */ +function resetDefaults() { + _UI.pMain.pFilter.oExcludeNonEditables.CHECKED = true; + _UI.pMain.pFilter.oExcludeDuplicates.CHECKED = true; + _UI.pMain.pFilter.oExcludeStreets.CHECKED = false; + _UI.pMain.pFilter.oExcludeOther.CHECKED = false; + _UI.pMain.pFilter.oExcludeNotes.CHECKED = false; + + _UI.pMain.pSearch.oIncludeYourEdits.CHECKED = false; + _UI.pMain.pSearch.oIncludeUpdatedBy.VALUE = ''; + _RT.$includeUpdatedByCache = {}; + _UI.pMain.pSearch.oIncludeUpdatedSince.VALUE = ""; + _RT.$includeUpdatedSinceTime = 0; + _UI.pMain.pSearch.oIncludeCityName.VALUE = ""; + _RT.$includeCityNameCache = {}; + _UI.pMain.pSearch.oIncludeChecks.VALUE = ""; + _RT.$includeChecksCache = {}; + + _UI.pSettings.pScanner.oSlowChecks.CHECKED = true; + _UI.pSettings.pScanner.oReportExt.CHECKED = true; + _UI.pSettings.pScanner.oHLReported.CHECKED = true; + // _UI.pSettings.pScanner.oShowLayers.CHECKED = false; + _UI.pSettings.pScanner.oSounds.CHECKED = false; + + _UI.pSettings.pCustom.oTemplate1.VALUE = ""; + _UI.pSettings.pCustom.oRegExp1.VALUE = ""; + _UI.pSettings.pCustom.oTemplate2.VALUE = ""; + _UI.pSettings.pCustom.oRegExp2.VALUE = ""; +} + +/** + * Compare checks IDs (array sort) + */ +function cmpCheckIDs(a, b) { + var checkA = _RT.$checks[a], checkB = _RT.$checks[b]; + if (checkA.SEVERITY !== checkB.SEVERITY) + return checkB.SEVERITY - checkA.SEVERITY; + + // if severities are the same - sort by the name + var cmp = checkA.TITLE.localeCompare(checkB.TITLE); + if (!cmp) + return a - b; + return cmp; +} +/** + * Check no city + */ +function checkNoCity(str) { + return str ? str : "No City"; +} +/** + * Check no street + */ +function checkNoStreet(str) { + return str ? str : "No Street"; +} + +/** + * Returns new max severity after filters has been applied + */ +function getFilteredSeverity(oldSeverity, checkID, checkToHL) { + if (!_UI.pMain.pSearch.oIncludeChecks.VALUE) + return oldSeverity; + + var check = _RT.$checks[checkID]; + if (checkToHL && check.REPORTONLY) + return 0; + + var textSeverity = getTextSeverity(check.SEVERITY).toUpperCase(); + + var cache = _RT.$includeChecksCache; + /** @const */ + var hash = checkID; + + if (hash in cache) { + if (cache[hash]) + return check.SEVERITY; + } + else { + var forChecks = _UI.pMain.pSearch.oIncludeChecks.VALUE; + // try top country + var ccode = _RT.$cachedTopCCode; + var options = trO(check.OPTIONS, ccode); + var curTitle = exSOS(check.TITLE, options, "titleEN"); + try { + cache[hash] = false; + if (_WV.checkAccessFor(forChecks, + function (e) { + if (/^#?\d+$/.test(e)) { + if ("#" === e.charAt(0)) + e = e.slice(1) + // try to compare IDs + return +checkID === +e; + } + if (e.toUpperCase() === textSeverity) + return true; + // escape user input + e = escRE(e); + var r = new RegExp("^" + e + "$", "i"); + return r.test(curTitle); + }) + ) { + cache[hash] = true; + return check.SEVERITY; + } + } + catch (e) { + // delete cache[hash]; + // error("Error in 'Reported as' search field:\n\n" + e); + } + } + + return 0; +} +/** + * Returns new max severity after filters has been applied + */ +function getFilteredSeverityObj(oldSeverity, checkIDs, checkToHL) { + if (!_UI.pMain.pSearch.oIncludeChecks.VALUE) + return oldSeverity; + + var ret = 0; + for (var cid in checkIDs) { + if (!checkIDs.hasOwnProperty(cid)) + continue; + + var check = _RT.$checks[cid]; + if (getFilteredSeverity(check.SEVERITY, cid, checkToHL)) { + if (ret < check.SEVERITY) { + ret = check.SEVERITY; + + if (_RT.$curMaxSeverity === ret) + return ret; + } + } + } // for all check IDs + + return ret; +} +/** + * Check filter + */ +function checkFilter(severity, segmentCopy, seenSegments) { + if (seenSegments) { + if (segmentCopy.$segmentID in seenSegments + && _UI.pMain.pFilter.oExcludeDuplicates.CHECKED) + return false; + seenSegments[segmentCopy.$segmentID] = null; + } + + if ((RR_STREET === segmentCopy.$typeRank + || RR_SERVICE === segmentCopy.$typeRank) + && _UI.pMain.pFilter.oExcludeStreets.CHECKED) + return false; + if (RR_SERVICE > segmentCopy.$typeRank + && _UI.pMain.pFilter.oExcludeOther.CHECKED) + return false; + if (!segmentCopy.$isEditable + && _UI.pMain.pFilter.oExcludeNonEditables.CHECKED) + return false; + if (RS_NOTE === severity + && _UI.pMain.pFilter.oExcludeNotes.CHECKED) + return false; + + if (segmentCopy.$userID !== _RT.$topUser.$userID + && !_UI.pMain.pSearch.oIncludeYourEdits.NODISPLAY + && _UI.pMain.pSearch.oIncludeYourEdits.CHECKED) + return false; + + if (!_UI.pMain.pSearch.oIncludeUpdatedBy.NODISPLAY + && _UI.pMain.pSearch.oIncludeUpdatedBy.VALUE) { + var cache = _RT.$includeUpdatedByCache; + var hash = segmentCopy.$userID; + + if (hash in cache) { + if (!cache[hash]) + return false; + } + else { + var forUser = _UI.pMain.pSearch.oIncludeUpdatedBy.VALUE; + var curUser = _REP.$users[segmentCopy.$userID]; + try { + cache[hash] = false; + // check if the user tries to match another user + if (curUser !== _RT.$topUser.$userName + && !_RT.$topUser.$isCM + && !_RT.$topUser.$isSuperUser) + return false; + // check if CM match country ID + if (_RT.$topUser.$isCM + && -1 === _RT.$topUser.$countryIDs.indexOf(segmentCopy.$countryID)) + return false; + if (!_WV.checkAccessFor(forUser, + function (e) { + // escape user input + e = escRE(e); + // substitute 'me' + e = e.replace(/(^|\b)(me|i)($|\b)/gi, _RT.$topUser.$userName); + + var r = new RegExp("^" + e + "$", "i"); + return r.test(curUser); + }) + ) + return false; + cache[hash] = true; + } + catch (e) { } + } + } + + if (segmentCopy.$updated && _UI.pMain.pSearch.oIncludeUpdatedSince.VALUE) { + try { + if (!_RT.$includeUpdatedSinceTime) + _RT.$includeUpdatedSinceTime = + new Date(_UI.pMain.pSearch.oIncludeUpdatedSince.VALUE).getTime(); + if (segmentCopy.$updated < _RT.$includeUpdatedSinceTime) + return false; + } + catch (e) { } + } + + if (_UI.pMain.pSearch.oIncludeCityName.VALUE) { + if (!segmentCopy.$cityID) + return false; + + var cache = _RT.$includeCityNameCache; + var hash = segmentCopy.$cityID; + + if (hash in cache) { + if (!cache[hash]) + return false; + } + else { + var forCity = _UI.pMain.pSearch.oIncludeCityName.VALUE; + var curCity = _REP.$cities[segmentCopy.$cityID]; + try { + cache[hash] = false; + if (!_WV.checkAccessFor(forCity, + function (e) { + // escape user input + e = escRE(e); + var r = new RegExp("^" + e + "$", "i"); + return r.test(curCity); + }) + ) + return false; + cache[hash] = true; + } + catch (e) { } + } + } + + return true; +} + +/** + * Translate object by country code + */ +function trO(obj, ccode) { + if (obj) + return _I18n.getValueOC(obj, ccode); +} + +/** + * Get check options + */ +function getCheckOptions(checkID, ccode) { + return _I18n.getValueOC(_RT.$checks[checkID].OPTIONS, ccode); +} + +/** + * Get Left Direction + */ +function trLeft(dir) { + if ("ltr" === dir) + return "left"; + else + return "right"; +} + +/** + * Get Right Direction + */ +function trRight(dir) { + if ("ltr" === dir) + return "right"; + else + return "left"; +} + +/** + * Translate string + */ +function trS(label) { + return _I18n.getString(label); +} + +/** + * Translate string with options + */ +function trSO(label, options) { + return _I18n.expandSO(_I18n.getString(label), options); +} + +/** + * Expand string with options and substitute + */ +function exSOS(str, options, subst) { + if (options && _I18n.$defLng === _RT.$lng && options[subst]) + return _I18n.expandSO(options[subst], options); + else + return _I18n.expandSO(str, options); +} + +/** + * Returns text representation of severity + */ +function getTextSeverity(sev) { + switch (sev) { + case RS_WARNING: return "warning"; + case RS_ERROR: return "error"; + case RS_CUSTOM1: return "custom1"; + case RS_CUSTOM2: return "custom2"; + } + return "note"; +} + +// critical functions in BASIC +CFADD(clearTimeout, "setAttribute", Function); + +/************************************************************************* + * HANDLERS + *************************************************************************/ + +/** + * Update UI handler + */ +function onUpdateUI(e) { + async(F_UPDATEUI, e); +} + +/** + * Show Available Checks Handler (only sync calls for popup blocker!) + */ +function onShowChecks(e) { + sync(F_SHOWREPORT, RF_LIST); +} + +/** + * Create language pack (only sync calls for popup blocker!) + */ +function onCreatePack(e) { + sync(F_SHOWREPORT, RF_CREATEPACK); +} + +/** + * Show Report Handler (only sync calls for popup blocker!) + */ +function onShowReport(e) { + sync(F_SHOWREPORT, RF_HTML); +} + +/** + * Show BB Report Handler (only sync calls for popup blocker!) + */ +function onShareReport(e) { + sync(F_SHOWREPORT, RF_BB); +} + +/** + * Warning handler + */ +function onWarning(e) { + async(F_ONWARNING, e); +} + +/** + * Login changed handler + */ +function onLogin() { + async(F_ONLOGIN); +} + +/** + * Merge End Handler + */ +function onMergeEnd() { + _RT.$isMapChanged = true; + // kill WDs + CFCALLTHIS1(CF_CLEARTIMEOUT, CFGET(CF_WINDOW), _RT.$WDmoveID); + CFCALLTHIS1(CF_CLEARTIMEOUT, CFGET(CF_WINDOW), _RT.$WDloadID); + + async(F_ONMERGEEND); +} + +/** + * Move End Handler + */ +function onMoveEnd() { + if (RTStateIs(ST_RUN) || RTStateIs(ST_CONTINUE)) + async(F_ONMOVEEND); + else + ForceHLAllSegments(); +} + +/** + * Load Start Handler + */ +function onLoadStart() { + async(F_ONLOADSTART); +} + +/** + * Change Layer Handler + */ +function onChangeLayer(e) { + sync(F_ONCHANGELAYER, e); +} + +/** + * Segments Changed Handler + */ +function onSegmentsChanged(e) { + _RT.$isMapChanged = true; + sync(F_ONSEGMENTSCHANGED, e); +} +/** + * Segments Removed Handler + */ +function onSegmentsRemoved(e) { + _RT.$isMapChanged = true; + if (1 === e.length) + if (RTStateIs(ST_STOP) || RTStateIs(ST_PAUSE)) + sync(F_ONSEGMENTSCHANGED, e); +} +/** + * Segments Removed Handler + */ +function onSegmentsAdded(e) { + _RT.$isMapChanged = true; +} + +/** + * Nodes Changed Handler + */ +function onNodesChanged(e) { + _RT.$isMapChanged = true; + sync(F_ONNODESCHANGED, e); +} +/** + * Nodes Removed Handler + */ +function onNodesRemoved(e) { + _RT.$isMapChanged = true; + if (1 === e.length) + if (RTStateIs(ST_STOP) || RTStateIs(ST_PAUSE)) + sync(F_ONNODESCHANGED, e); +} + +// critical functions in HANDLERS +CFADD(setTimeout, window, CFGET(CF_DOCUMENT)["createElement"]); diff --git a/src/data.js b/src/data.js new file mode 100644 index 0000000..f8c93a9 --- /dev/null +++ b/src/data.js @@ -0,0 +1,549 @@ +/* + * data.js -- WME Validator global data + * Copyright (C) 2013-2018 Andriy Berestovskyy + * + * This file is part of WME Validator: https://github.com/WMEValidator/ + * + * WME Validator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * WME Validator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with WME Validator. If not, see . + */ + +/************************************************************************* + * DEFINES + *************************************************************************/ + +/** + * Debug mode + */ +/** @define {boolean} */ +var DEF_DEBUG = false; + +/************************************************************************* + * THUI + *************************************************************************/ +var _THUI = {}; +/** + * THUI Element Types + */ +/** const */ +_THUI.NONE = 1; +/** const */ +_THUI.DIV = 2; +/** const */ +_THUI.NUMBER = 3; +/** const */ +_THUI.RADIO = 4; +/** const */ +_THUI.CHECKBOX = 5; +/** const */ +_THUI.BUTTON = 6; +/** const */ +_THUI.TEXT = 7; +/** const */ +_THUI.PASSWORD = 8; +/** const */ +_THUI.DATE = 9; + +/************************************************************************* + * DATA + *************************************************************************/ + +/** + * Global private namespace for encrypted functions + * @type {Object} + */ +var _WV = {}; + +/** + * Global WME Beta Version Flag + */ +var WME_BETA = false; + + +/** + * Global Login Flag + */ +_WV.$loggedIn = 0; + + +/** + * Private array of functions + * @dict + */ +_WV.$functions = []; + +/** + * User interface + * See F_LOGIN + * @struct + */ +var _UI = {}; + +/** + * Runtime private variables + * See F_LOGIN + * @struct + */ +var _RT = {} + +/** + * Global report object + * @struct + */ +var _REP = {} + +/** + * Global names + */ +/** @const */ +var GL_SHOWLAYERS = false; +/** @const */ +var GL_LAYERNAME = WV_NAME; +/** @const */ +//var GL_LAYERBIT = 13; +/** @const */ +var GL_LAYERUNAME = WV_NAME_NO_SPACE; +/** @const */ +var GL_LAYERACCEL = "toggle" + WV_NAME_NO_SPACE; +/** @const */ +var GL_LAYERSHORTCUT = "A+v"; +/** @const */ +var GL_TBCOLOR = "WMETB_color"; +/** @const */ +var GL_TBPREFIX = "WMETB"; +/** @const */ +var GL_WMECHCOLOR = "WMECH_color"; +/** @const */ +var GL_NOTECOLOR = "#30E"; +/** @const */ +var GL_NOTEBGCOLOR = "#EEF"; +/** @const */ +var GL_WARNINGCOLOR = "#DA0"; +//var GL_WARNINGCOLOR = "#960"; +/** @const */ +var GL_WARNINGBGCOLOR = "#FFE"; +/** @const */ +var GL_ERRORCOLOR = "#E00"; +/** @const */ +var GL_ERRORBGCOLOR = "#FEE"; +/** @const */ +var GL_CUSTOM1COLOR = "#0A0"; +/** @const */ +var GL_CUSTOM1BGCOLOR = "#EFE"; +/** @const */ +var GL_CUSTOM2COLOR = "#09E"; +/** @const */ +var GL_CUSTOM2BGCOLOR = "#EFF"; +/** @const */ +var GL_VISITEDCOLOR = "#0E0"; +/** @const */ +var GL_VISITEDBGCOLOR = "#EFE"; +/** @const */ +var GL_NOID = "No ID"; +/** @const */ +var GL_GRAYCOLOR = "#AAA"; +/** @const */ +var GL_TODOMARKER = "TODO: "; + +/** + * Segment road types + */ +/** @const */ +var RT_RUNWAY = 19; +/** @const */ +var RR_RUNWAY = 1; +/** @const */ +var RT_RAILROAD = 18; +/** @const */ +var RR_RAILROAD = 2; +/** @const */ +var RT_STAIRWAY = 16; +/** @const */ +var RR_STAIRWAY = 3; +/** @const */ +var RT_BOARDWALK = 10; +/** @const */ +var RR_BOARDWALK = 4; +/** @const */ +var RT_TRAIL = 5; +/** @const */ +var RR_TRAIL = 5; + +/** @const */ +var RT_PRIVATE = 17; +/** @const */ +var RR_PRIVATE = 6; +/** @const */ +var RT_PARKING = 20; +/** @const */ +var RR_PARKING = 7; +/** @const */ +var RT_DIRT = 8; +/** @const */ +var RR_DIRT = 8; +/** @const */ +var RT_SERVICE = 21; +/** @const */ +var RR_SERVICE = 9; + +/** @const */ +var RT_STREET = 1; +/** @const */ +var RR_STREET = 10; +/** @const */ +var RT_PRIMARY = 2; +/** @const */ +var RR_PRIMARY = 11; + +/** @const */ +var RT_RAMP = 4; +/** @const */ +var RR_RAMP = 12; +/** @const */ +var RT_MINOR = 7; +/** @const */ +var RR_MINOR = 13; +/** @const */ +var RT_MAJOR = 6; +/** @const */ +var RR_MAJOR = 14; +/** @const */ +var RT_FREEWAY = 3; +/** @const */ +var RR_FREEWAY = 15; + +/** + * Segment directions + */ +/** @const */ +var DIR_UNKNOWN = 0; +/** @const */ +var DIR_AB = 1; +/** @const */ +var DIR_BA = 2; +/** @const */ +var DIR_TWO = 3; + +/** + * States constants + * 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b (directions), + * report formats: 0x59f111f1, 0x923f82a4, 0xab1c5ed5 + */ +/** @const */ +var ST_STOP = 0x428a2f98; +/** @const */ +var ST_RUN = 0x71374491; +/** @const */ +var ST_PAUSE = 0xb5c0fbcf; +/** @const */ +var ST_CONTINUE = 0xe9b5dba5; + +/** + * Pan directions + * (IDs from states) + */ +/** @const */ +var DIR_L2R = 0x3956c25b; +/** @const */ +var DIR_R2L = -0x3956c25b; + +/** + * Report formats + * (IDs from states) + */ +/** @const */ +var RF_HTML = 0x59f111f1; +/** @const */ +var RF_BB = 0x923f82a4; +/** @const */ +var RF_LIST = 0xab1c5ed5; +// internal: updates max severity for the given filters +/** @const */ +var RF_UPDATEMAXSEVERITY = 1; +/** @const */ +var RF_CREATEPACK = 2; + +/** + * Report traversal commands + */ +/** @const */ +var RT_STOP = 1; +/** @const */ +var RT_NEXTCHECK = 2; + + +/** + * Report severities + */ +/** @const */ +var RS_NOTE = 1; +/** @const */ +var RS_WARNING = 2; +/** @const */ +var RS_ERROR = 3; +/** @const */ +var RS_CUSTOM2 = 4; +/** @const */ +var RS_CUSTOM1 = 5; +/** @const */ +var RS_MAX = 6; + +/** + * Limits + */ +/** @const */ +var LIMIT_PERCHECK = 300; +/** @const */ +var LIMIT_TOLERANCE = 6; // ~5m +/** @const */ +var LIMIT_DEBUG = 20; + +/** + * Check IDs + */ +/** @const */ +var CK_TBFIRST = 1; +/** @const */ +var CK_TBLAST = 9; +/** @const */ +var CK_WMECHFIRST = 13; +/** @const */ +var CK_WMECHLAST = 22; +// Used to generate type checks +/** @const */ +var CK_TYPEFIRST = 70; +/** @const */ +var CK_TYPELAST = 72; +// Used to match and validate the segment: +/** @const */ +var CK_MATCHFIRST = 128; +/** @const */ +var CK_MATCHLAST = 139; +// Used to generate custom checks descriptions +/** @const */ +var CK_CUSTOMFIRST = 130; +/** @const */ +var CK_CUSTOMLAST = 139; +// Used to generate lock checks +/** @const */ +var CK_LOCKFIRST = 150; +/** @const */ +var CK_LOCKLAST = 154; +// Used to generate street type-name checks +/** @const */ +var CK_STREETTNFIRST = 160; +/** @const */ +var CK_STREETTNLAST = 167; +// Used to generate regexp street name checks +/** @const */ +var CK_STREETNAMEFIRST = 170; +/** @const */ +var CK_STREETNAMELAST = 175; +// Used to generate regexp city name checks +/** @const */ +var CK_CITYNAMEFIRST = 190; +/** @const */ +var CK_CITYNAMELAST = 193; +// Used to generate mirror node checks +/** @const */ +var CK_MIRRORFIRST = 200; +/** @const */ +var CK_MIRRORLAST = 201; + +/** + * Element IDs and classes + * + * 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + * 0x06ca6351, 0x14292967, + * 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, + + * 0x5cb0a9dc, 0x76f988da, + */ +/** The whole UI @const */ +var CL_UI = 0xa831c66d - 1; +/** Common prefix @const */ +var ID_PREFIX = 0x983e5152; +/** Waze-style tabs @const */ +var CL_TABS = 0xa831c66d; +/** Panel with fixed height @const */ +var CL_PANEL = 0xb00327c8; +/** Buttons @const */ +var CL_BUTTONS = 0xbf597fc7; +/** @const */ +var CL_MSG = 0xc6e00bf3; +/** @const */ +var CL_MSGY = 0xd5a79147; +/** @const */ +var CL_TRANSLATETIP = 0xd5a79147 + 1; + +/** @const */ +var AS_LICENSE = 0x14292967 - 2; +/** @const */ +var AS_VERSION = 0x14292967 - 1; +/** Filter: @const */ +var AS_NONEDITABLES = 0x14292967; +/** @const */ +var AS_DUPLICATES = 0x14292967 + 1; +/** @const */ +var AS_STREETS = 0x14292967 + 2; +/** @const */ +var AS_OTHERS = 0x14292967 + 3; +/** @const */ +var AS_NOTES = 0x14292967 + 4; + +/** @const */ +var AS_YOUREDITS = 0x14292967 + 5; +/** @const */ +var AS_UPDATEDSINCE = 0x14292967 + 6; +/** @const */ +var AS_CITYNAME = 0x14292967 + 7; +/** @const */ +var AS_CHECKS = 0x14292967 + 8; +/** @const */ +var AS_UPDATEDBY = 0x14292967 + 9; + +/** Settings: @const */ +var AS_SOUNDS = 0x06ca6351; +/** @const */ +var AS_HLISSUES = 0x06ca6351 + 1; +/** @const */ +var AS_SLOWCHECKS = 0x06ca6351 + 2; +/** @const */ +var AS_CUSTOM1TEMPLATE = 0x06ca6351 + 4; +/** @const */ +var AS_CUSTOM1REGEXP = 0x06ca6351 + 5; +/** @const */ +var AS_CUSTOM2TEMPLATE = 0x06ca6351 + 6; +/** @const */ +var AS_CUSTOM2REGEXP = 0x06ca6351 + 7; +/** @const */ +var AS_REPORTEXT = 0x06ca6351 + 8; + +/** @const */ +var ID_PROPERTY = 0xe49b69c1; +/** @const */ +var ID_PROPERTY_DISABLED = 0xe49b69c1 + 1; +/** @const */ +var CL_COLLAPSE = 0xefbe4786; +/** @const */ +var CL_NOTE = 0x0fc19dc6; +/** @const */ +var CL_WARNING = 0x240ca1cc; +/** @const */ +var CL_ERROR = 0x2de92c6f; +/** @const */ +var CL_CUSTOM1 = 0x2de92c6f + 1; +/** @const */ +var CL_CUSTOM2 = 0x2de92c6f + 2; +/** @const */ +var CL_RIGHTTIP = 0x4a7484aa; +/** @const */ +var CL_RIGHTTIPPOPUP = 0x4a7484aa + 1; +/** @const */ +var CL_RIGHTTIPDESCR = 0x4a7484aa + 2; + +/** + * Autosave + */ +/** Auto-save object name @const */ +var AS_NAME = WV_NAME_; + +/** + * Sizes + */ +/** @const */ +var SZ_PANEL_HEIGHT = 190; + +/** + * Scan constants + */ +/** @const */ +var SCAN_ZOOM = 4; +/** @const */ +var SCAN_STEP = 100; + +/** + * HL constants + */ +/** @const */ +var HL_WIDTH = 30; +/** @const */ +var HL_OPACITY = 0.4; + +/** + * Array indexes + */ +/** @const */ +var I_SEVERITY = 0; +/** @const */ +var I_SEGMENTCOPY = 1; +/** @const */ +var I_ISTBCOLOR = 2; +/** @const */ +var I_ISWMECHCOLOR = 3; +/** @const */ +var I_ISPARTIAL = 4; +/** @const */ +var I_CITYID = 5; + +/** + * Check options + */ +/** @const */ +var CO_MIN = 0; +/** @const */ +var CO_REGEXP = 0; +/** @const */ +var CO_STRING = 1; +/** @const */ +var CO_NUMBER = 2; +/** @const */ +var CO_BOOL = 3; +/** @const */ +var CO_MAX = 3; + +/** + * Watch Dogs + */ +/** @const */ +var WD_SHORT = 5; +/** @const */ +var WD_LONG = 1e4; + +/** + * Shortcuts + */ +/** Waze @const */ +var Wa = null; +/** new W @const */ +var nW = null; +/** Waze.map @const */ +var WM = null; +/** Waze.loginManager @const */ +var WLM = null; +/** Waze.selectionManager @const */ +var WSM = null; +/** Waze.model @const */ +var WMo = null; +/** Waze.controller @const */ +var WC = null; +/** unsafeWindow @const */ +var UW = null; +/** requite @const */ +var R = null; + +// critical functions in DATA +CFADD(async, "onclick", document); + +/** @const */ +var CF_CALLEE = arguments.callee; diff --git a/src/enc.js b/src/enc.js new file mode 100644 index 0000000..e4eed71 --- /dev/null +++ b/src/enc.js @@ -0,0 +1,203 @@ +/* + * enc.js -- WME Validator self-encryption support + * Copyright (C) 2013-2018 Andriy Berestovskyy + * + * This file is part of WME Validator: https://github.com/WMEValidator/ + * + * WME Validator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * WME Validator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with WME Validator. If not, see . + */ + +/************************************************************************* + * ENCRYPTED FUNCTIONS + *************************************************************************/ + +/** + * Function IDs + * password protected functions must be < 0 + * + * 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, + * 0x81c2c92e, 0x92722c85, + * 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, + * 0xf40e3585, 0x106aa070, + * 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + * 0x5b9cca4f, 0x682e6ff3, + * 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, + * 0xbef9a3f7, 0xc67178f2 + + * 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, + * 0x9bdc06a7, 0xc19bf174, + */ +/** @const */ +var F_INIT = 0x27b70a85; +/** @const */ +var F_LOGIN = 0x2e1b2138; +/** @const */ +var F_UPDATEUI = 0x4d2c6dfc; +/** @const */ +var F_ONWARNING = 0x53380d13; +/** @const */ +var F_ONSEGMENTSCHANGED = 0x650a7354; +/** @const */ +//var F_HAX = 0x766a0abb; +/** @const */ +var F_ONNODESCHANGED = 0x81c2c92e; +/** @const */ +var F_LOGOUT = 0x92722c85; +/** @const */ +var F_ONLOGIN = 0xa2bfe8a1; +/** @const */ +//var F_ONSEGMENTSREMOVED = 0xa81a664b; +/** @const */ +//var F_HAXRED = 0xc24b8b70; +/** @const */ +var F_HAXBLUE = 0xc76c51a3; +/** @const */ +var F_ONRUN = 0xd192e819; +/** @const */ +var F_ONMERGEEND = 0xd6990624; +/** @const */ +var F_STOP = 0xf40e3585; +/** @const */ +var F_PAUSE = 0x106aa070; +/** @const */ +var F_VALIDATE = 0x19a4c116; +/** @const */ +var F_LAYERSON = 0x1e376c08; +/** @const */ +var F_LAYERSOFF = 0x2748774c; +/** @const */ +var F_ONLOADSTART = 0x34b0bcb5; +/** @const */ +var F_ONMOVEEND = 0x391c0cb3; +/** @const */ +//var F_CHECKSEGMENTS = 0x84c87814; +/** @const */ +var F_SHOWREPORT = 0x90befffa; +/** @const */ +var F_ONCHANGELAYER = 0xa4506ceb; + +/** @const */ +var F_ARRAY = [ + F_INIT, F_LOGIN, F_UPDATEUI, F_ONWARNING, /*F_HAX,*/ + F_LOGOUT, F_ONLOGIN, /*F_HAXRED,*/ F_HAXBLUE, F_ONRUN, + F_STOP, F_PAUSE, F_VALIDATE, F_LAYERSON, F_LAYERSOFF, + F_ONLOADSTART, F_ONMOVEEND, + F_SHOWREPORT, F_ONMERGEEND, F_ONCHANGELAYER, + F_ONSEGMENTSCHANGED, F_ONNODESCHANGED +]; +/** @const */ +var F_LENGTH = F_ARRAY.length; + +/************************************************************************* + * CRITICAL FUNCTIONS + *************************************************************************/ + +// HEADER +/** @const */ +var CF_UNSHIFT = 0; +/** @const */ +var CF_SLENGTH = 1; +/** @const */ +var CF_TOSTRING = 2; +/** @const */ +var CF_CHARCODEAT = 3; +// DATA +/** @const */ +var CF_ASYNC = 4; +/** @const */ +var CF_SONCLICK = 5; +/** @const */ +var CF_DOCUMENT = 6; +// BASIC +/** @const */ +var CF_CLEARTIMEOUT = 7; +/** @const */ +var CF_SSETATTRIBUTE = 8; +/** @const */ +var CF_FUNCTION = 9; +// HANDLERS +/** @const */ +var CF_SETTIMEOUT = 10; +/** @const */ +var CF_WINDOW = 11; +/** @const */ +var CF_CREATEELEMENT = 12; +// ENCRYPTED +/** @const */ +var CF_CLASSCODE = 13; +/** @const */ +var CF_REPLACE = 14; +/** @const */ +var CF_SCONSOLE = 15; +/** @const */ +var CF_APPLY = 16; +/** @const */ +var CF_CALL = 17; +// ASYNC +/** @const */ +var CF_JSONPARSE = 18; +/** @const */ +var CF_DDE = 19; +// LIB +/** @const */ +var CF_SLOG = 20; +/** @const */ +var CF_EVAL = 21; +/** @const */ +var CF_SERROR = 22; +/** @const */ +var CF_SWARN = 23; +// END +/** @const */ +var CF_TEADECRYPT = 24; +// ASYNC after timeout: +/** @const */ +var CF_CONSOLE = 25; +/** @const */ +var CF_LOG = 26; +/** @const */ +var CF_ERROR = 27; +// DDE: +/** @const */ +var CF_WARN = 28; +/** @const */ +var CF_ALERT = 29; + +// array of mutators +var CFM = [CF_SLENGTH, CF_UNSHIFT]; + +/** + * Critical functions calls + */ +function CFGET(cfunc) { return CFA[CFM[cfunc]] } +function CFABSENT(cfunc) { return cfunc >= CFA[CFGET(CF_SLENGTH)] } +function CFCALL(cfunc) { return CFGET(cfunc)() } +function CFCALL1(cfunc, param1) { return CFGET(cfunc)(param1) } +function CFCALL2(cfunc, param1, param2) { return CFGET(cfunc)(param1, param2) } +function CFCALL3(cfunc, param1, param2, param3) { return CFGET(cfunc)(param1, param2, param3) } +function CFAPPLYTHIS(cfunc, obj) { return CFGET(cfunc).apply(obj) } +function CFAPPLYTHIS1(cfunc, obj, param1) { return CFGET(cfunc).apply(obj, param1) } +function CFCALLTHIS1(cfunc, obj, param1) { return CFAPPLYTHIS1(cfunc, obj, [param1]) } +function CFCALLTHIS2(cfunc, obj, param1, param2) { return CFAPPLYTHIS1(cfunc, obj, [param1, param2]) } +function CFCALLTHIS3(cfunc, obj, param1, param2, param3) { return CFAPPLYTHIS1(cfunc, obj, [param1, param2, param3]) } +/** @param {...*} args */ +function CFADD(args) { + var us = CFGET(CF_UNSHIFT); + var len = CFGET(CF_SLENGTH); + var ra = [].slice.call(arguments, 0).reverse(); + CFAPPLYTHIS1(CF_UNSHIFT, CFA, ra); + for (var i = CFM[len], lim = CFM[len] + ra[len]; + i < lim; i++) + us.call(CFM, i); +} diff --git a/src/encrypted.js b/src/encrypted.js new file mode 100644 index 0000000..ceb73b1 --- /dev/null +++ b/src/encrypted.js @@ -0,0 +1,1365 @@ +/* + * encrypted.js -- WME Validator encrypted functions and handlers + * Copyright (C) 2013-2018 Andriy Berestovskyy + * + * This file is part of WME Validator: https://github.com/WMEValidator/ + * + * WME Validator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * WME Validator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with WME Validator. If not, see . + */ + +/************************************************************************* + * ENCRYPTED FUNCTIONS + *************************************************************************/ + +/** + * On Segments Changed Handler + */ +_WV.$functions[F_ONSEGMENTSCHANGED] = function(e) +{ + // add nearby segments to _RT.$revalidate + var changedNodes = []; + for(var i = 0; i < e.length; i++) + { + var nodeIDs = [e[i].attributes.fromNodeID, e[i].attributes.toNodeID]; + for(var j = 0; j < nodeIDs.length; j++) + { + var nodeID = nodeIDs[j]; + if(!nodeID) continue; + var node = WMo.nodes.get(nodeID); + if(node) + changedNodes.push(node); + } + } + if(changedNodes.length) + sync(F_ONNODESCHANGED, changedNodes); +} + +/** + * On Nodes Changed Handler + */ +_WV.$functions[F_ONNODESCHANGED] = function(e) +{ + // add nearby segments to _RT.$revalidate + var reHL = false; + for(var i = 0; i < e.length; i++) + { + var ids = e[i].attributes.segIDs; + for(var j = 0; j < ids.length; j++) + _RT.$revalidate[ids[j]] = true, + reHL = true; + } + // revalidate all the segments + if(reHL) + HLAllSegments(); +} + +/** + * On Change Layer Handler + */ +_WV.$functions[F_ONCHANGELAYER] = function(e) +{ + if(-1 !== e.layer.id.indexOf(GL_TBPREFIX)) + { + if(!e.layer.visibility) + { + for(var segmentID in WMo.segments.objects) + { + if(!WMo.segments.objects.hasOwnProperty(segmentID)) continue; + delete WMo.segments.objects[segmentID][GL_TBCOLOR]; + } + } + ForceHLAllSegments(); + } + else + if(GL_LAYERUNAME === e.layer.uniqueName + && e.layer.visibility !== _UI.pSettings.pScanner.oHLReported.CHECKED + ) + { + // switch Validator on/off + _RT.$switchValidator = true; + async(F_UPDATEUI); + } +} + +/** + * On Move End Handler + */ +_WV.$functions[F_ONMOVEEND] = function() +{ + var c = WM.getCenter(); + + if(-1 === _RT.$WDmoveID + && -1 === _RT.$WDloadID + && c.equals(_RT.$nextCenter) + ) + _RT.$WDmoveID = CFCALLTHIS2(CF_SETTIMEOUT, + CFGET(CF_WINDOW), onMergeEnd, WD_SHORT); + else + { + // autopause on user move + if(RTStateIs(ST_RUN) && !_RT.$firstStep + && !c.equals(_RT.$nextCenter) + && !c.equals(_RT.$startCenter) + ) + { + _RT.$curMessage = { + TEXT: trS("msg.autopaused.text"), + TITLE: trS("msg.autopaused.tip"), + }; + async(F_PAUSE); + } + } + + _RT.$moveEndCenter = c; +} + +/** + * On Load Start Handler + */ +_WV.$functions[F_ONLOADSTART] = function() +{ + var c = WM.getCenter(); + + // kill move WD + CFCALLTHIS1(CF_CLEARTIMEOUT, CFGET(CF_WINDOW), _RT.$WDmoveID); + + if(-1 === _RT.$WDloadID + && c.equals(_RT.$nextCenter) + ) + _RT.$WDloadID = CFCALLTHIS2(CF_SETTIMEOUT, + CFGET(CF_WINDOW), onMergeEnd, WD_LONG); + + _RT.$WDmoveID = -1; + +} + +/** + * Switch all layers but roads off + */ +_WV.$functions[F_LAYERSOFF] = function() +{ + // TODO: +// Waze.Config.segments.zoomToRoadType[SCAN_ZOOM] = -1; + + _RT.$HLlayer.destroyFeatures(); +// if(_RT.$layersVisibility || _UI.pSettings.pScanner.oShowLayers.CHECKED) + if(_RT.$layersVisibility || GL_SHOWLAYERS) + return; + WM.layers.forEach(function(el) { + if(el.displayInLayerSwitcher && GL_LAYERUNAME !== el.uniqueName) + { + if(el.getVisibility()) + _RT.$layersVisibility += "T"; + else + _RT.$layersVisibility += "F"; + + el.setVisibility(false); + } + }); +} + +/** + * Switch all layers back on + */ +_WV.$functions[F_LAYERSON] = function() +{ +// if(!_RT.$layersVisibility || _UI.pSettings.pScanner.oShowLayers.CHECKED) + if(!_RT.$layersVisibility || GL_SHOWLAYERS) + return; + var j = 0; + WM.layers.forEach(function(el) { + if(el.displayInLayerSwitcher && GL_LAYERUNAME !== el.uniqueName) + { + if(_RT.$layersVisibility.length > j) + { + el.setVisibility("T" === _RT.$layersVisibility.charAt(j)); + j++; + } + } + }); + _RT.$layersVisibility = ""; +} + +/** + * Pause scanning + */ +_WV.$functions[F_PAUSE] = function() +{ + if(!RTStateIs(ST_RUN)) + return; + + beep(50, "square"); + // update max severity + sync(F_SHOWREPORT, RF_UPDATEMAXSEVERITY); + setRTState(ST_PAUSE); + async(F_LAYERSON); +} + + +/** + * Stop scanning + */ +_WV.$functions[F_STOP] = function() +{ + if(!RTStateIs(ST_STOP)) + { + beep(100, "square"); + // restore current view + if(_RT.$startCenter) + { + WM.panTo(_RT.$startCenter); + WM.zoomTo(_RT.$startZoom); + } + if(!_REP.$maxSeverity) + _RT.$curMessage = { + TEXT: trS("msg.noissues.text"), + TITLE: trS("msg.noissues.tip"), + }; + } + + // update max severity + sync(F_SHOWREPORT, RF_UPDATEMAXSEVERITY); + setRTState(ST_STOP); + async(F_LAYERSON); +} + +/** + * Encrypted Merge End Handler + */ +_WV.$functions[F_ONMERGEEND] = function() +{ + /** @const */ + var c = WM.getCenter(); + + // skip all but next center runs + if(RTStateIs(ST_RUN) && _RT.$nextCenter && !c.equals(_RT.$nextCenter)) + return; + /** @const */ + var e = WM.getExtent(); + /** @const */ + var ew = e.getWidth(); + /** @const */ + var eh = e.getHeight(); + /** @const */ + var ew2 = ew/2; + /** @const */ + var eh2 = eh/2; + var s = _RT.$startExtent; + if(!s) s = new UW.OpenLayers.Bounds(); + /** @const */ + var cx = c.lon; + /** @const */ + var cy = c.lat; + /** @const */ + var dir = Math.round(_RT.$direction/Math.abs(_RT.$direction)); + /** @const */ + var sw = s.getWidth(); + /** @const */ + var sh = s.getHeight(); + + // calculate real step X and Y + /** @const */ + var kxMax = Math.ceil(sw/(ew*SCAN_STEP/100)); + /** @const */ + var stepX = sw/kxMax; + /** @const */ + var kyMax = Math.ceil(sh/(eh*SCAN_STEP/100)); + /** @const */ + var stepY = sh/kyMax; + + if(RTStateIs(ST_CONTINUE)) + { + if(_RT.$nextCenter) + { + setRTState(ST_RUN); + // restore current view and continue + WM.zoomTo(SCAN_ZOOM); + WM.panTo(_RT.$nextCenter); + clearWD(); + return; + } + // no saved position - rerun + async(F_ONRUN); + return; + } + // Highlight reported segments + if(!RTStateIs(ST_RUN)) + { + HLAllSegments(); + return; + } + + async(F_UPDATEUI); + + if(_RT.$firstStep) + { + // make first step + _RT.$firstStep = false; + var newX = s.left + ew2; + var newY = s.top - eh2; + // save current view for pause + _RT.$nextCenter = new UW.OpenLayers.LonLat(newX, newY); + WM.zoomTo(SCAN_ZOOM); + WM.panTo(_RT.$nextCenter); + clearWD(); + return; + } + + // do the job! + sync(F_VALIDATE, false); + + /////////////////////////////////////////////////////////////////////// + // New X coordinate + + // 1. Get nearest position + var deltaX = Number.MAX_VALUE; + var deltaY = Number.MAX_VALUE; + var kx = 0; + var ky = 0; + for(var i = 0;;i++) + { + var x = s.left + ew2 + i*stepX; + var y = s.top - eh2 - i*stepY; + if(x > s.right && y < s.bottom) break; + + var cd = Math.abs(x - cx); + if(cd < deltaX) deltaX = cd, kx = i; + + cd = Math.abs(y - cy); + if(cd < deltaY) deltaY = cd, ky = i; + } + updateTimer(ST_RUN); + var curStep = ky*kxMax + (0 < dir? kx : kxMax - kx); + if(4 < curStep) + { + if(0 === curStep%5) + { + //var maxStep = (kyMax*(kxMax - 1)); + var maxStep = kyMax*kxMax; + var minETA = (maxStep / curStep - 1) * _RT.$timer.$secInRun / 60; + var strMsg = (1 > minETA) ? + trS("msg.scanning.text.soon") + : trSO("msg.scanning.text", {"n": Math.round(minETA)}) + ; + _RT.$curMessage = { + TEXT: strMsg, + TITLE: trS("msg.scanning.tip"), + }; + } + } + + // 2. Make an X step + kx = kx + dir; + + // 3. Check if new X is within start extent + var newX = s.left + ew2 + kx*stepX; + if(newX < s.left || newX > s.right + // or if center is closer to the start border that the edge + || Math.abs(newX - s.left) < Math.abs(newX - ew2 - s.left) + || Math.abs(newX - s.right) < Math.abs(newX + ew2 - s.right) + ) + { + // step back + newX = s.left + ew2 + (kx - dir)*stepX; + // change direction + _RT.$direction = -_RT.$direction; + // make an Y step + ky++; + } + + // 4. Check if new Y is within start extent + var newY = s.top - eh2 - ky*stepY; + if(newY < s.bottom + // or if center is closer to the start border that the edge + || Math.abs(newY - s.bottom) < Math.abs(newY - eh2 - s.bottom) + ) + { + // finished! + // check if any editable segment was found + if(!_REP.$isEditableFound && _UI.pMain.pFilter.oExcludeNonEditables.CHECKED) + _RT.$reportEditableNotFound = true; + + async(F_STOP); + return; + } + + _RT.$nextCenter = new UW.OpenLayers.LonLat(newX, newY); + // pan map + WM.zoomTo(SCAN_ZOOM); + WM.panTo(_RT.$nextCenter); + clearWD(); +} + +/** + * Encrypted Run Handler + */ +_WV.$functions[F_ONRUN] = function() +{ + // clear error flag + clearErrorFlag(); + + if(RTStateIs(ST_RUN)) + return; + + async(F_LAYERSOFF); + + _RT.$curMessage = { + TEXT: trS("msg.starting.text"), + TITLE: trS("msg.starting.tip"), + }; + setRTState(ST_RUN); + clearWD(); + _RT.$direction = DIR_L2R; + _RT.$firstStep = true; + + // save current view + var e = WM.getExtent(); + _RT.$startExtent = e; + _RT.$startCenter = WM.getCenter(); + _RT.$startZoom = WM.getZoom(); + _RT.$nextCenter = null; + _RT.$moveEndCenter = null; + +// clearReport(); + + _RT.$nextCenter = new UW.OpenLayers.LonLat(e.left, e.top); + WM.panTo(_RT.$nextCenter); + WM.zoomTo(SCAN_ZOOM); +} + +/** + * Encrypted Login Handler + */ +_WV.$functions[F_ONLOGIN] = function() +{ + if(WLM.user) + { + if(!_WV.$loggedIn) + { + // set the flag and do login + _WV.$loggedIn = true; + async(F_LOGIN); + } + } + else + { + if(_WV.$loggedIn) + { + // reset the flag and do logout + _WV.$loggedIn = false; + async(F_LOGOUT); + } + else + // we have no user and no flag is set + log("waiting for login..."); + } +} + +/** + * Show hax messages + */ + /* +_WV.$functions[F_HAX] = function(l) +{ + if(!DEF_DEBUG) + { + var msgs = [ + "Integrity check failed. Please reinstall " + WV_NAME + "!", +// "Are you trying to hack?\n\nPlease reinstall " + WV_NAME + "!", +// WV_NAME.toUpperCase() + " CAN DELETE ALL THE SEGMENTS\nif you keep trying to hack it!" + ]; + + if(_WV.$loggedIn) + { + if(_RT.$haxMessage < msgs.length) + warning("\n" + msgs[_RT.$haxMessage++]); + else + _RT.$haxMessage = 0; + + async(F_HAX, l, 1e5); + } + } +} +*/ + +/** + * Show hax messages + */ + /* +_WV.$functions[F_HAXRED] = function(l) +{ + alert("Please report " + WV_NAME + " CODE RED: " + l); + // destroy UI + _UI = {}; + // logout + async(F_LOGOUT); + // uninstall login/logout handler + WLM.events.un({ + "afterloginchanged": onLogin, + "login": onLogin + }); + // every 100 sec +// async(F_HAX, l, 1e5); +} +*/ + +/** + * Show hax messages + */ +_WV.$functions[F_HAXBLUE] = function(l) +{ + alert("Please report " + WV_NAME + " CODE BLUE: " + l); + // destroy UI + _UI = {}; + // uninstall login/logout handler + WLM.events.un({ + "afterloginchanged": onLogin, + "login": onLogin + }); + // every 100 sec +// async(F_HAX, l, 1e5); +} + +/** + * Init + */ +_WV.$functions[F_INIT] = function() +{ + var relDate = new Date(WV_RELEASE_VALID); + var nowDate = Date.now(); + if(0 > relDate.getTime() - nowDate) + { + // destroy UI + _UI = {}; + error("This build of " + WV_NAME + " has expired. Please upgrade!"); + return; + } +/* + // check critical functions + if(!OFF_DEF_SUPERUSERS) + { + var critSum = 0; + CFA.forEach(function(e){critSum += e.toString().length}); + if(critSum != CF_SUMREDC && critSum != CF_SUMREDF) + { + async(F_HAXRED, (critSum - CF_SUMREDC) + + "," + (critSum - CF_SUMREDF), 1e3); + return; + } + } +*/ + + // init shortcuts + UW = window; + Wa = UW.Waze; + nW = UW.W; + WLM = nW.loginManager; + WSM = nW.selectionManager; + WM = nW.map; + WMo = nW.model; + WC = nW.controller; + if(!Wa || !WLM || !WSM || !WM || !WMo || !WC || !$("#user-tabs")) + { + log("waiting for WME...") + async(F_INIT, null, 1e3); + return; + } + + // detect new WME version + if(classCodeDefined(UW.require)) + { + R = UW.require; + if (W.map.mapState._getLayerVisibilityBitmask) { + WME_BETA = true; + } + } + + // Google Analytics + var _gaq = UW["_gaq"]; + if(_gaq) + { + _gaq.push([WV_NAME_ + "._setAccount", "UA-46853768-3"]); + _gaq.push([WV_NAME_ + "._setDomainName", "waze.com"]); + _gaq.push([WV_NAME_ + "._trackPageview"]); + } + + // check critical functions calee + if(!DEF_DEBUG) + { + var critSum = CF_CALLEE.toString().length; + if(critSum != CF_SUMBLUE) + { + async(F_HAXBLUE, (critSum - CF_SUMBLUE), 1e3); + return; + } + } + + _WV.$loggedIn = false; + // install login/logout handler + WLM.events.on({ + "loginStatus": onLogin, + "login": onLogin + }); + + // do login or wait for user + async(F_ONLOGIN); + + /////////////////////////////////////////////////////////////////////// + // Custom RegExp + // build regexps + _WV.buildRegExp = function(checkID, options, strRegExp) + { + try{ + // skip leading white spaces + while(strRegExp && ' ' === strRegExp.charAt(0)) + strRegExp = strRegExp.substr(1); + + if(strRegExp) + { + // debug + if('D' === strRegExp.charAt(0)) + { + strRegExp = strRegExp.substr(1); + options[CO_NUMBER] = 1; + } + else + options[CO_NUMBER] = 0; + // negate + if('!' === strRegExp.charAt(0)) + { + strRegExp = strRegExp.substr(1); + options[CO_BOOL] = true; + } + else + options[CO_BOOL] = false; + + if('/' === strRegExp.charAt(0)) + strRegExp = strRegExp.substr(1); + var strRegExpOptions = ''; + var arrMatch = strRegExp.match(/\/([igmy]*)$/); + if(arrMatch) + { + strRegExpOptions = arrMatch[1]; + strRegExp = strRegExp.slice(0, -arrMatch[0].length); + } + options[CO_REGEXP] = + new RegExp(strRegExp, strRegExpOptions); + } + else + { + options[CO_BOOL] = false; + options[CO_NUMBER] = 0; + options[CO_REGEXP] = null; + } + } catch(e) + { + error(trSO("err.regexp", {"n": checkID}) + '\n\n' + e); + options[CO_BOOL] = false; + options[CO_NUMBER] = 0; + options[CO_REGEXP] = null; + } + } // buildRegExp + + /////////////////////////////////////////////////////////////////////// + // Simple address and simple city classes + + /** + * Simple city object constructor + * @struct + * @param {number=} objID + * @constructor + */ + _WV.SimpleCITY = function(objID) + { + /** @type {number} */ + this.$hash = 0; + /** @type {number} */ + this.$cityID = 0; + /** @type {string} */ + this.$city = ""; + /** @type {string} */ + this.$state = ""; + /** @type {number} */ + this.$countryID = 0; + /** @type {string} */ + this.$country = ""; + + if(objID) + { + this.$cityID = objID; + var oc = WMo.cities.get(objID); + if(oc) + { + this.$city = oc.attributes.isEmpty ? "" : oc.attributes.name; + var o = WMo.states.get(oc.attributes.stateID); + if(o) + this.$state = o.name; + this.$countryID = oc.attributes.countryID; + o = WMo.countries.get(oc.attributes.countryID); + if(o) + this.$country = o.name; + } + this.$hash = this.$cityID + this.$countryID; + + Object.defineProperties(this, { + $hash: { writable: false }, + $cityID: { writable: false }, + $state: { writable: false }, + $countryID: { writable: false }, + $country: { writable: false }, + }); + } + } + /** + * Check access for the check + * @param {number} checkID + * @returns {boolean} + */ + _WV.SimpleCITY.prototype.isOkFor = function(checkID) + { + // check for global access + if(!_RT.$isGlobalAccess) return false; + + /** @const */ + var rep = _RT.$checks[checkID]; + + if(!rep.$cache) rep.$cache = {}; + /** @const */ + var cache = rep.$cache; + + /** @const */ + var forCity = rep.FORCITY; + /** @const */ + var hash = forCity? this.$hash : this.$countryID; + + if(hash in cache) return cache[hash]; + + // check access lists and create a hash record + + // no access by default + cache[hash] = false; + + ////////////////////////////////////////////////////////////////////////// + // Check the level + /** @const */ + var forLevel = rep.FORLEVEL; + if(forLevel && forLevel > _RT.$topUser.$userLevel) return false; + + ////////////////////////////////////////////////////////////////////////// + // Check the city + if(forCity) + { + /** @const */ + var curCity = this.$city.toUpperCase(); + if(!_WV.checkAccessFor(forCity, + function(e) {return e.toUpperCase() === curCity}) + ) + return false; + } + + ////////////////////////////////////////////////////////////////////////// + // Check the user + /** @const */ + var forUser = rep.FORUSER; + if(forUser) + { + /** @const */ + var curUser = _RT.$topUser.$userName.toUpperCase(); + if(!_WV.checkAccessFor(forUser, + function(e) {return e.toUpperCase() === curUser}) + ) + return false; + } + + ////////////////////////////////////////////////////////////////////////// + // Check the country + /** @const */ + var forCountry = rep.FORCOUNTRY; + if(forCountry) + { + /** @const */ + var curCountry = this.$country.toUpperCase(); + + if(!_WV.checkAccessFor(forCountry, function(e) { + if(e in _I18n.$code2country) + return _I18n.$code2country[e] === curCountry; + error("Please report: fc=" + e); + return false; + })) + return false; + } + + // the tool has passed all the checks + cache[hash] = true; + return true; + } + + /** + * Simple address object constructor + * @struct + * @param {number} objID + * @constructor + */ + _WV.SimpleADDRESS = function(objID) + { + /** @type {number} */ + this.$streetID = 0; + /** @type {string} */ + this.$street = ""; + + if(objID) + { + this.$streetID = objID; + var o = WMo.streets.get(objID); + if(o) + { + this.$street = o.isEmpty ? "" : o.name; + _WV.SimpleCITY.call(this, o.cityID); + } + else + { + this.$street = GL_NOID; + _WV.SimpleCITY.call(this, 0); + } + } + + Object.defineProperties(this, { + $streetID: { writable: false }, + }); + } + _WV.SimpleADDRESS.prototype = new _WV.SimpleCITY; + _WV.SimpleADDRESS.prototype.constructor = _WV.SimpleADDRESS; +} + +/** + * Warn User + */ +_WV.$functions[F_ONWARNING] = function(e) +{ + // update document + _THUI.viewToDoc(_UI); + + // get target object + var target = _THUI.getByDOM(_UI, e.target); + + // check if target has a warning + if(target && target.CHECKED && target.WARNING) + warning(target.WARNING); + + async(F_UPDATEUI); +} + +/** + * Update User Interface + */ +_WV.$functions[F_UPDATEUI] = function(e) +{ + /** + * Destroj HLs + */ + function destroyHLs(){ + _RT.$HLedObjects = {}; + _RT.$HLlayer.destroyFeatures(); + } + /** + * Updates report buttons + */ + function updateReportButtons() + { + if(RTStateIs(ST_RUN)||RTStateIs(ST_CONTINUE)) + { + btns.bReport.CLASS = "btn btn-default"; + btns.bReport.DISABLED = true; + btns.bReportBB.DISABLED = true; + return; + } + + if(!_REP.$maxSeverity) + { + btns.bReport.CLASS = "btn btn-default"; + btns.bReport.DISABLED = true; + btns.bReportBB.DISABLED = true; + } + else + { + switch(_REP.$maxSeverity) + { + case RS_NOTE: + btns.bReport.CLASS = "btn btn-info"; + break; + case RS_WARNING: + btns.bReport.CLASS = "btn btn-warning"; + break; + case RS_ERROR: + btns.bReport.CLASS = "btn btn-danger"; + break; + case RS_CUSTOM1: + btns.bReport.CLASS = "btn btn-success"; + break; + case RS_CUSTOM2: + btns.bReport.CLASS = "btn btn-primary"; + break; + } + btns.bReport.DISABLED = false; + btns.bReportBB.DISABLED = false; + } + + // update start button + if(3 < WM.getZoom()) + { + btns.bScan.CLASS = "btn btn-default"; + btns.bScan.DISABLED = true; + btns.bScan.TITLE = trS("button.scan.tip.NA"); + } + else + { + btns.bScan.CLASS = "btn btn-success"; + btns.bScan.DISABLED = false; + btns.bScan.TITLE = trS("button.scan.tip"); + } + + // update clear button + if(_REP.$isLimitPerCheck) + { + btns.bClear.CLASS = "btn btn-danger"; + btns.bClear.TITLE = trS("button.clear.tip.red"); + } + else + { + btns.bClear.CLASS = "btn btn-default"; + btns.bClear.TITLE = trS("button.clear.tip"); + } + + // update validator title + if(_UI.pSettings.pScanner.oHLReported.CHECKED) + { +// TODO: France + _UI.pMain.pTabs.tMain.TEXT = ' ' + WV_SHORTNAME + ':'; +// _UI.pMain.pTabs.tMain.TEXT = ' Validator:'; + _UI.pMain.pTabs.tMain.TITLE = trS("tab.switch.tip.off"); + } + else + { + _UI.pMain.pTabs.tMain.TEXT = ' ' + WV_SHORTNAME + ':'; + _UI.pMain.pTabs.tMain.TITLE = trS("tab.switch.tip.on"); + } + _UI.pMain.pTabs.tMain.TITLE += '\n' + WV_NAME + " Version " + WV_VERSION; + + } + /** + * Returns simple representation of top city and country + */ + function getTopCity() + { + var i = WMo.segments.topCityID; + if(i) return new _WV.SimpleCITY(i); + + return new _WV.SimpleCITY(0); + } + _RT.$topCity = getTopCity(); + if(_RT.$topCity.$country) + _RT.$cachedTopCCode = _I18n.getCountryCode(_RT.$topCity.$country.toUpperCase()); + + // update document + _THUI.viewToDoc(_UI); + + // check global access + _RT.$isGlobalAccess = true; + if(!_RT.$topCity.isOkFor(0)) + _RT.$isGlobalAccess = false; + + if(!_RT.$isGlobalAccess) + { + _UI.pMain.NODISPLAY = 1; + _UI.pSettings.NODISPLAY = 1; + _UI.pTips.NODISPLAY = 1; + _UI.pNoAccess.NODISPLAY = 0; + _THUI.docToView(_UI); + return; + } + else + if(!_UI.pNoAccess.NODISPLAY) + { + _UI.pMain.NODISPLAY = 0; + _UI.pTips.NODISPLAY = 0; + _UI.pNoAccess.NODISPLAY = 1; + } + + // check for Toolbox + if(_RT.oReportToolbox.NA + && null !== document.getElementById(_RT.oReportToolbox.FORID)) + { + _RT.oReportToolbox.CHECKED = true; + _RT.oReportToolbox.NA = false; + clearReport(); + async(ForceHLAllSegments, null, 1e3); + } + // check for WMECH + if(_RT.oReportWMECH.NA + && null !== document.getElementById(_RT.oReportWMECH.FORID)) + { + _RT.oReportWMECH.CHECKED = true; + _RT.oReportWMECH.NA = false; + clearReport(); + async(ForceHLAllSegments, null, 1e3); + } + // build custom RegExps + var customOptions = _RT.$checks[128].OPTIONS[_I18n.$defLng]; + if(customOptions[CO_STRING] !== _UI.pSettings.pCustom.oTemplate1.VALUE + || _RT.$RegExp1 !== _UI.pSettings.pCustom.oRegExp1.VALUE) + { + customOptions[CO_STRING] = _UI.pSettings.pCustom.oTemplate1.VALUE; + if(customOptions[CO_STRING]) + { + clearErrorFlag(); + _RT.$RegExp1 = _UI.pSettings.pCustom.oRegExp1.VALUE; + _WV.buildRegExp(128, customOptions, + _UI.pSettings.pCustom.oRegExp1.VALUE); + } + else + customOptions[CO_REGEXP] = null; + } + customOptions = _RT.$checks[129].OPTIONS[_I18n.$defLng]; + if(customOptions[CO_STRING] !== _UI.pSettings.pCustom.oTemplate2.VALUE + || _RT.$RegExp2 !== _UI.pSettings.pCustom.oRegExp2.VALUE) + { + customOptions[CO_STRING] = _UI.pSettings.pCustom.oTemplate2.VALUE; + if(customOptions[CO_STRING]) + { + clearErrorFlag(); + _RT.$RegExp2 = _UI.pSettings.pCustom.oRegExp2.VALUE; + _WV.buildRegExp(128, customOptions, + _UI.pSettings.pCustom.oRegExp2.VALUE); + } + else + customOptions[CO_REGEXP] = null; + } + // set user checks flag + if(_RT.$checks[128].OPTIONS[_I18n.$defLng][CO_REGEXP]) + _RT.$curMaxSeverity = RS_CUSTOM1; + else if(_RT.$checks[129].OPTIONS[_I18n.$defLng][CO_REGEXP]) + _RT.$curMaxSeverity = RS_CUSTOM2; + else + _RT.$curMaxSeverity = RS_ERROR; + + /////////////////////////////////////////////////////////////////////// + // Button handlers + if(e) + { + // check if a button pressed + switch(_THUI.getByDOM(_UI, e.target)) + { + case _UI.pMain.pTabs.tMain: + _RT.$switchValidator = true; + break; + case _UI.pSettings.pCustom.oTemplate1: + case _UI.pSettings.pCustom.oRegExp1: + case _UI.pSettings.pCustom.oTemplate2: + case _UI.pSettings.pCustom.oRegExp2: + _RT.$isMapChanged = true; + clearReport(); + async(ForceHLAllSegments); + break; + case _UI.pMain.pFilter.oExcludeNonEditables: + case _UI.pMain.pFilter.oExcludeDuplicates: + case _UI.pMain.pFilter.oExcludeStreets: + case _UI.pMain.pFilter.oExcludeOther: + case _UI.pMain.pFilter.oExcludeNotes: + case _UI.pMain.pSearch.oIncludeYourEdits: + case _UI.pMain.pSearch.oIncludeUpdatedBy: + case _UI.pMain.pSearch.oIncludeUpdatedSince: + case _UI.pMain.pSearch.oIncludeCityName: + case _UI.pMain.pSearch.oIncludeChecks: + _RT.$includeUpdatedByCache = {}; + _RT.$includeUpdatedSinceTime = 0; + _RT.$includeCityNameCache = {}; + _RT.$includeChecksCache = {}; + // update max severity + async(F_SHOWREPORT, RF_UPDATEMAXSEVERITY); + // highlight segments + async(ForceHLAllSegments); + break; + case _UI.pMain.pButtons.bScan: + async(F_ONRUN); + break; + case _UI.pMain.pButtons.bStop: + async(F_STOP); + break; + case _UI.pMain.pButtons.bClear: + _RT.$isMapChanged = true; + clearErrorFlag(); + clearReport(); + destroyHLs(); + break; + case _UI.pMain.pButtons.bPause: + _RT.$curMessage = { + TEXT: trS("msg.paused.text"), + TITLE: trS("msg.paused.tip"), + }; + async(F_PAUSE); + break; + case _UI.pMain.pButtons.bContinue: + // clear error flag + clearErrorFlag(); + if(!RTStateIs(ST_PAUSE)) + break; + // start report over + if(LIMIT_TOTAL < _REP.$counterTotal) + clearReport(); + + async(F_LAYERSOFF); + _RT.$curMessage = { + TEXT: trS("msg.continuing.text"), + TITLE: trS("msg.continuing.tip"), + }; + setRTState(ST_CONTINUE); + // restore start view and continue + if(_RT.$startCenter) + { + WM.zoomTo(_RT.$startZoom); + WM.panTo(_RT.$startCenter); + } + clearWD(); + break; + case _UI.pMain.pButtons.bSettings: + _UI.pMain.NODISPLAY = true; + _UI.pSettings.NODISPLAY = false; + _RT.$curMessage = { + TEXT: trS("msg.settings.text"), + TITLE: trS("msg.settings.tip"), + }; + break; + case _UI.pSettings.pButtons.bReset: + resetDefaults(); + _RT.$curMessage = { + TEXT: trS("msg.reset.text"), + TITLE: trS("msg.reset.tip"), + }; + // update max severity + sync(F_SHOWREPORT, RF_UPDATEMAXSEVERITY); + // highlight segments + async(ForceHLAllSegments); + break; + case _UI.pSettings.pButtons.bBack: + _UI.pMain.NODISPLAY = false; + _UI.pSettings.NODISPLAY = true; + break; + case _UI.pSettings.pScanner.oHLReported: + // switch Validator on/off + _UI.pSettings.pScanner.oHLReported.CHECKED = + !_UI.pSettings.pScanner.oHLReported.CHECKED; + _RT.$switchValidator = true; + break; + } + + // check if target has a warning + async(F_ONWARNING, e); + } + + // check switch flag + if(_RT.$switchValidator) + { + _UI.pSettings.pScanner.oHLReported.CHECKED = + !_UI.pSettings.pScanner.oHLReported.CHECKED; + if(_UI.pSettings.pScanner.oHLReported.CHECKED) + { + ForceHLAllSegments(); + _RT.$HLlayer.setVisibility(true); + } + else + { + ForceHLAllSegments(); + destroyHLs(); + _RT.$HLlayer.setVisibility(false); + } + _RT.$switchValidator = false; + } + + // check if any editable segment was found + if(_RT.$reportEditableNotFound) + { + _RT.$reportEditableNotFound = false; + _UI.pMain.pFilter.oExcludeNonEditables.CHECKED = false; + info("'Exclude non-editable segments' filter option has been removed because the area you just scanned has no editable segments.\n\nNow just click 'Show report' to view the report!"); + } + /////////////////////////////////////////////////////////////////////// + // Update panels + _UI.pMain.pHelp.NODISPLAY = !_UI.pMain.pTabs.tHelp.CHECKED; + _UI.pMain.pFilter.NODISPLAY = !_UI.pMain.pTabs.tFilter.CHECKED; + _UI.pMain.pSearch.NODISPLAY = !_UI.pMain.pTabs.tSearch.CHECKED; + _UI.pSettings.pScanner.NODISPLAY = !_UI.pSettings.pTabs.tScanner.CHECKED; + _UI.pSettings.pCustom.NODISPLAY = !_UI.pSettings.pTabs.tCustom.CHECKED; + _UI.pSettings.pAbout.NODISPLAY = !_UI.pSettings.pTabs.tAbout.CHECKED; + + /////////////////////////////////////////////////////////////////////// + // Update buttons on panel + if(_UI.pSettings.pTabs.tAbout.CHECKED) + { + _UI.pSettings.pButtons.bReset.NODISPLAY = 1; + _UI.pSettings.pButtons.bList.NODISPLAY = 0; + _UI.pSettings.pButtons.bWizard.NODISPLAY = 0; + } + else + { + _UI.pSettings.pButtons.bReset.NODISPLAY = 0; + _UI.pSettings.pButtons.bList.NODISPLAY = 1; + _UI.pSettings.pButtons.bWizard.NODISPLAY = 1; + } + + /////////////////////////////////////////////////////////////////////// + // Update UI on state + var btns = _UI.pMain.pButtons; + switch(getRTState()) + { + case ST_CONTINUE: + case ST_RUN: + btns.bScan.NODISPLAY = true; + btns.bPause.NODISPLAY = false; + btns.bContinue.NODISPLAY = true; + btns.bStop.NODISPLAY = false; + btns.bClear.NODISPLAY = true; + updateReportButtons(); + btns.bSettings.DISABLED = true; + _UI.pMain.pFilter._DISABLED = true; + _UI.pMain.pSearch._DISABLED = true; + break; + case ST_STOP: + btns.bScan.NODISPLAY = false; + btns.bPause.NODISPLAY = true; + btns.bContinue.NODISPLAY = true; + btns.bStop.NODISPLAY = true; + btns.bClear.NODISPLAY = false; + if(isEmpty(_RT.$seen)) + btns.bClear.DISABLED = true; + else + btns.bClear.DISABLED = false; + updateReportButtons(); + if(_REP.$maxSeverity && !_UI.pMain.NODISPLAY) + _RT.$curMessage = { + TEXT: trS("msg.finished.text"), + TITLE: trS("msg.finished.tip"), + CLASS: CL_MSGY + }; + btns.bSettings.DISABLED = false; + _UI.pMain.pFilter._DISABLED = false; + _UI.pMain.pSearch._DISABLED = false; + break; + case ST_PAUSE: + btns.bScan.NODISPLAY = true; + btns.bPause.NODISPLAY = true; + btns.bContinue.NODISPLAY = false; + btns.bContinue.DISABLED = false; + btns.bStop.NODISPLAY = false; + btns.bClear.NODISPLAY = true; + updateReportButtons(); + btns.bSettings.DISABLED = false; + _UI.pMain.pFilter._DISABLED = false; + _UI.pMain.pSearch._DISABLED = false; + break; + } + + /////////////////////////////////////////////////////////////////////// + // Update current message + + if(RTStateIs(ST_STOP) && !_REP.$maxSeverity) + { + // always display a zoom out message + if(!_UI.pMain.NODISPLAY) + { + if(3 < WM.getZoom()) + _RT.$curMessage = { + TEXT: _UI.pSettings.pScanner.oHLReported.CHECKED ? + trS("msg.pan.text") + : trS("msg.zoomout.text"), + TITLE: "", + CLASS: CL_MSGY + }; + else + _RT.$curMessage = { + TEXT: trS("msg.click.text"), + TITLE: "", + CLASS: CL_MSGY + }; + } + } + + + _UI.pTips.TEXT = _RT.$curMessage.TEXT; + if(_RT.$curMessage.TITLE) + _UI.pTips.TITLE = _RT.$curMessage.TITLE; + else + _UI.pTips.TITLE = ""; + if(_RT.$curMessage.CLASS) + _UI.pTips.CLASS = _RT.$curMessage.CLASS; + else + _UI.pTips.CLASS = CL_MSG; + + // save values to local storage + var storageObj = _THUI.saveValues(_UI); + storageObj[AS_VERSION] = WV_VERSION; + storageObj[AS_LICENSE] = WV_LICENSE_VERSION; + try + { + window.localStorage.setItem(AS_NAME, + Tea.encrypt(JSON.stringify(storageObj), AS_PASSWORD)); + } + catch (err) {} + + // update view + _THUI.docToView(_UI); +} + +/** + * Logout a user + */ +_WV.$functions[F_LOGOUT] = function() +{ + log("logout"); + // destroy UI + _UI = {}; + + // unregister event handlers + WMo.events.un({ + "mergeend": onMergeEnd, + }); + WM.events.un({ + "moveend": onMoveEnd, + "zoomend": HLAllSegments, + "changelayer": onChangeLayer, + }); + WSM.events.un({ + "selectionchanged": ForceHLAllSegments + }); + WC.events.un({ + "loadstart": onLoadStart, + }); + + // monitor segments and nodes changes + WMo.segments.events.un({ + "objectsadded": onSegmentsAdded, + "objectschanged": onSegmentsChanged, + "objectsremoved": onSegmentsRemoved, + }); + WMo.nodes.events.un({ + "objectschanged": onNodesChanged, + "objectsremoved": onNodesRemoved, + }); +} + +// critical functions in ENCRYPTED +CFADD(classCode, "".replace, "console", + CFGET(CF_FUNCTION)["apply"], CFGET(CF_FUNCTION)["call"]); + +// call the init function when the library is initialized +CFCALL3(CF_ASYNC, F_INIT, 0, 1); diff --git a/src/ext/base64.js b/src/ext/base64.js new file mode 100644 index 0000000..3c04e05 --- /dev/null +++ b/src/ext/base64.js @@ -0,0 +1,197 @@ +/* + * This is an early and modified version of TEA Block: + * https://github.com/chrisveness/crypto/ + * (c) Chris Veness 2002-2017 + * + * MIT Licence + */ + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Base64 class: Base 64 encoding / decoding (c) Chris Veness 2002-2012 */ +/* note: depends on Utf8 class */ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +var Base64 = {}; // Base64 namespace + +/* + * The next few functions are (c) Andriy Berestovskyy + */ +// Get base64 code from a char +// z-aZ-A:9-0/. +Base64.getCode = function (char) { + var c = 122 - char.charCodeAt(0); + return c - Math.floor((c - Math.floor(c / 26) * 6) / 26) * 6; +} + +// Get base 64 char from a code +// z-aZ-A:9-0/. +Base64.getChar = function (code) { + return String.fromCharCode(122 - code - Math.floor(code / 26) * 6); +} + +// Check if getChar corresponds to getCode +Base64.testCodes = function () { + // Get base 64 code from a char + var chars = ""; + for (var i = 0; i < 65; i++) { + chars += Base64.getChar(i); + } + + // check if unique + var codes = []; + for (var i = 0; i < chars.length; i++) { + var c = chars.charCodeAt(i); + if (codes[c]) { + codes[c]++; + window.console.log("Dub: " + i); + } + else + codes[c] = 1; + } + + // check is code == char + for (var i = 0; i < 65; i++) { + var ch = Base64.getChar(i); + var co = Base64.getCode(ch); + if (i != co) { + window.console.log("Mismatch: " + i); + } + } + + window.console.log(chars); +} + + +// Updated to use getCode/getChar functions by berestovskyy +/** @param {boolean=} utf8encode */ +Base64.encode = function (str, utf8encode) { // http://tools.ietf.org/html/rfc4648 + utf8encode = (typeof utf8encode == undefined) ? false : utf8encode; + var o1, o2, o3, bits, h1, h2, h3, h4, e = [], pad = '', c, plain, coded; + + plain = utf8encode ? Utf8.encode(str) : str; + + c = plain.length % 3; // pad string to length of multiple of 3 + if (c > 0) { while (c++ < 3) { pad += Base64.getChar(64); plain += '\0'; } } + // note: doing padding here saves us doing special-case packing for trailing 1 or 2 chars + + for (c = 0; c < plain.length; c += 3) { // pack three octets into four hexets + o1 = plain.charCodeAt(c); + o2 = plain.charCodeAt(c + 1); + o3 = plain.charCodeAt(c + 2); + + bits = o1 << 16 | o2 << 8 | o3; + + h1 = bits >> 18 & 0x3f; + h2 = bits >> 12 & 0x3f; + h3 = bits >> 6 & 0x3f; + h4 = bits & 0x3f; + + // use hextets to index into code string + e[c / 3] = Base64.getChar(h1) + Base64.getChar(h2) + Base64.getChar(h3) + Base64.getChar(h4); + } + coded = e.join(''); // join() is far faster than repeated string concatenation in IE + + // replace 'A's from padded nulls with '='s + coded = coded.slice(0, coded.length - pad.length) + pad; + + return coded; +} + +// Updated to use getCode/getChar functions by berestovskyy +/** @param {boolean=} utf8decode */ +Base64.decode = function (str, utf8decode) { + utf8decode = (typeof utf8decode == undefined) ? false : utf8decode; + var o1, o2, o3, h1, h2, h3, h4, bits, d = [], plain, coded; + + coded = utf8decode ? Utf8.decode(str) : str; + + + for (var c = 0; c < coded.length; c += 4) { // unpack four hexets into three octets + h1 = Base64.getCode(coded.charAt(c)); + h2 = Base64.getCode(coded.charAt(c + 1)); + h3 = Base64.getCode(coded.charAt(c + 2)); + h4 = Base64.getCode(coded.charAt(c + 3)); + + bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; + + o1 = bits >>> 16 & 0xff; + o2 = bits >>> 8 & 0xff; + o3 = bits & 0xff; + + d[c / 4] = String.fromCharCode(o1, o2, o3); + // check for padding + if (h4 == 0x40) d[c / 4] = String.fromCharCode(o1, o2); + if (h3 == 0x40) d[c / 4] = String.fromCharCode(o1); + } + plain = d.join(''); // join() is far faster than repeated string concatenation in IE + + return utf8decode ? Utf8.decode(plain) : plain; +} + +/** @const */ +Base64.code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; +//Base64.code = "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZefghijklmnopqrstuvwxyz"; +/** @param {boolean=} utf8encode */ +Base64.encodeStd = function (str, utf8encode) { // http://tools.ietf.org/html/rfc4648 + utf8encode = (typeof utf8encode == undefined) ? false : utf8encode; + var o1, o2, o3, bits, h1, h2, h3, h4, e = [], pad = '', c, plain, coded; + var b64 = Base64.code; + + plain = utf8encode ? Utf8.encode(str) : str; + + c = plain.length % 3; // pad string to length of multiple of 3 + if (c > 0) { while (c++ < 3) { pad += Base64.code[64]; plain += '\0'; } } + // note: doing padding here saves us doing special-case packing for trailing 1 or 2 chars + + for (c = 0; c < plain.length; c += 3) { // pack three octets into four hexets + o1 = plain.charCodeAt(c); + o2 = plain.charCodeAt(c + 1); + o3 = plain.charCodeAt(c + 2); + + bits = o1 << 16 | o2 << 8 | o3; + + h1 = bits >> 18 & 0x3f; + h2 = bits >> 12 & 0x3f; + h3 = bits >> 6 & 0x3f; + h4 = bits & 0x3f; + + // use hextets to index into code string + e[c / 3] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); + } + coded = e.join(''); // join() is far faster than repeated string concatenation in IE + + // replace 'A's from padded nulls with '='s + coded = coded.slice(0, coded.length - pad.length) + pad; + + return coded; +} +/** @param {boolean=} utf8decode */ +Base64.decodeStd = function (str, utf8decode) { + utf8decode = (typeof utf8decode == undefined) ? false : utf8decode; + var o1, o2, o3, h1, h2, h3, h4, bits, d = [], plain, coded; + var b64 = Base64.code; + + coded = utf8decode ? Utf8.decode(str) : str; + + + for (var c = 0; c < coded.length; c += 4) { // unpack four hexets into three octets + h1 = b64.indexOf(coded.charAt(c)); + h2 = b64.indexOf(coded.charAt(c + 1)); + h3 = b64.indexOf(coded.charAt(c + 2)); + h4 = b64.indexOf(coded.charAt(c + 3)); + + bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; + + o1 = bits >>> 16 & 0xff; + o2 = bits >>> 8 & 0xff; + o3 = bits & 0xff; + + d[c / 4] = String.fromCharCode(o1, o2, o3); + // check for padding + if (h4 == 0x40) d[c / 4] = String.fromCharCode(o1, o2); + if (h3 == 0x40) d[c / 4] = String.fromCharCode(o1); + } + plain = d.join(''); // join() is far faster than repeated string concatenation in IE + + return utf8decode ? Utf8.decode(plain) : plain; +} diff --git a/src/ext/sha256.js b/src/ext/sha256.js new file mode 100644 index 0000000..702b6e2 --- /dev/null +++ b/src/ext/sha256.js @@ -0,0 +1,120 @@ +/* + * This is an early version of SHA-256 implementation: + * https://github.com/chrisveness/crypto/ + * (c) Chris Veness 2002-2017 + * + * MIT Licence + */ + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* SHA-256 implementation in JavaScript | (c) Chris Veness 2002-2010 | www.movable-type.co.uk */ +/* - see http://csrc.nist.gov/groups/ST/toolkit/secure_hashing.html */ +/* http://csrc.nist.gov/groups/ST/toolkit/examples.html */ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +var Sha256 = {}; // Sha256 namespace + +/** @param {boolean=} utf8encode */ +Sha256.hash = function (msg, utf8encode) { + utf8encode = (typeof utf8encode == undefined) ? true : utf8encode; + + // convert string to UTF-8, as SHA only deals with byte-streams + if (utf8encode) msg = Utf8.encode(msg); + + // constants [4.2.2] + var K = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2]; + // initial hash value [5.3.1] + var H = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]; + + // PREPROCESSING + + msg += String.fromCharCode(0x80); // add trailing '1' bit (+ 0's padding) to string [5.1.1] + + // convert string msg into 512-bit/16-integer blocks arrays of ints [5.2.1] + var l = msg.length / 4 + 2; // length (in 32-bit integers) of msg + '1' + appended length + var N = Math.ceil(l / 16); // number of 16-integer-blocks required to hold 'l' ints + var M = new Array(N); + + for (var i = 0; i < N; i++) { + M[i] = new Array(16); + for (var j = 0; j < 16; j++) { // encode 4 chars per integer, big-endian encoding + M[i][j] = (msg.charCodeAt(i * 64 + j * 4) << 24) | (msg.charCodeAt(i * 64 + j * 4 + 1) << 16) | + (msg.charCodeAt(i * 64 + j * 4 + 2) << 8) | (msg.charCodeAt(i * 64 + j * 4 + 3)); + } // note running off the end of msg is ok 'cos bitwise ops on NaN return 0 + } + // add length (in bits) into final pair of 32-bit integers (big-endian) [5.1.1] + // note: most significant word would be (len-1)*8 >>> 32, but since JS converts + // bitwise-op args to 32 bits, we need to simulate this by arithmetic operators + M[N - 1][14] = ((msg.length - 1) * 8) / Math.pow(2, 32); M[N - 1][14] = Math.floor(M[N - 1][14]) + M[N - 1][15] = ((msg.length - 1) * 8) & 0xffffffff; + + + // HASH COMPUTATION [6.1.2] + + var W = new Array(64); var a, b, c, d, e, f, g, h; + for (var i = 0; i < N; i++) { + + // 1 - prepare message schedule 'W' + for (var t = 0; t < 16; t++) W[t] = M[i][t]; + for (var t = 16; t < 64; t++) W[t] = (Sha256.sigma1(W[t - 2]) + W[t - 7] + Sha256.sigma0(W[t - 15]) + W[t - 16]) & 0xffffffff; + + // 2 - initialise working variables a, b, c, d, e, f, g, h with previous hash value + a = H[0]; b = H[1]; c = H[2]; d = H[3]; e = H[4]; f = H[5]; g = H[6]; h = H[7]; + + // 3 - main loop (note 'addition modulo 2^32') + for (var t = 0; t < 64; t++) { + var T1 = h + Sha256.Sigma1(e) + Sha256.Ch(e, f, g) + K[t] + W[t]; + var T2 = Sha256.Sigma0(a) + Sha256.Maj(a, b, c); + h = g; + g = f; + f = e; + e = (d + T1) & 0xffffffff; + d = c; + c = b; + b = a; + a = (T1 + T2) & 0xffffffff; + } + // 4 - compute the new intermediate hash value (note 'addition modulo 2^32') + H[0] = (H[0] + a) & 0xffffffff; + H[1] = (H[1] + b) & 0xffffffff; + H[2] = (H[2] + c) & 0xffffffff; + H[3] = (H[3] + d) & 0xffffffff; + H[4] = (H[4] + e) & 0xffffffff; + H[5] = (H[5] + f) & 0xffffffff; + H[6] = (H[6] + g) & 0xffffffff; + H[7] = (H[7] + h) & 0xffffffff; + } + + // return Sha256.toHexStr(H[0]) + Sha256.toHexStr(H[1]) + Sha256.toHexStr(H[2]) + Sha256.toHexStr(H[3]) + + // Sha256.toHexStr(H[4]) + Sha256.toHexStr(H[5]) + Sha256.toHexStr(H[6]) + Sha256.toHexStr(H[7]); + return Base64.encode(Tea.longsToStr(H)); +} + +// critical functions in END +CFADD(Tea.decrypt); + +Sha256.ROTR = function (n, x) { return (x >>> n) | (x << (32 - n)); } +Sha256.Sigma0 = function (x) { return Sha256.ROTR(2, x) ^ Sha256.ROTR(13, x) ^ Sha256.ROTR(22, x); } +Sha256.Sigma1 = function (x) { return Sha256.ROTR(6, x) ^ Sha256.ROTR(11, x) ^ Sha256.ROTR(25, x); } +Sha256.sigma0 = function (x) { return Sha256.ROTR(7, x) ^ Sha256.ROTR(18, x) ^ (x >>> 3); } +Sha256.sigma1 = function (x) { return Sha256.ROTR(17, x) ^ Sha256.ROTR(19, x) ^ (x >>> 10); } +Sha256.Ch = function (x, y, z) { return (x & y) ^ (~x & z); } +Sha256.Maj = function (x, y, z) { return (x & y) ^ (x & z) ^ (y & z); } + +// +// hexadecimal representation of a number +// (note toString(16) is implementation-dependant, and +// in IE returns signed numbers when used on full words) +// +Sha256.toHexStr = function (n) { + var s = "", v; + for (var i = 7; i >= 0; i--) { v = (n >>> (i * 4)) & 0xf; s += v.toString(16); } + return s; +} diff --git a/src/ext/tea-block.js b/src/ext/tea-block.js new file mode 100644 index 0000000..e872ac8 --- /dev/null +++ b/src/ext/tea-block.js @@ -0,0 +1,106 @@ +/* + * This is an early version of TEA Block: + * https://github.com/chrisveness/crypto/ + * (c) Chris Veness 2002-2017 + * + * MIT Licence + */ + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Block TEA (xxtea) Tiny Encryption Algorithm implementation in JavaScript */ +/* (c) Chris Veness 2002-2012: www.movable-type.co.uk/tea-block.html */ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Algorithm: David Wheeler & Roger Needham, Cambridge University Computer Lab */ +/* http://www.cl.cam.ac.uk/ftp/papers/djw-rmn/djw-rmn-tea.html (1994) */ +/* http://www.cl.cam.ac.uk/ftp/users/djw3/xtea.ps (1997) */ +/* http://www.cl.cam.ac.uk/ftp/users/djw3/xxtea.ps (1998) */ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +var Tea = {}; // Tea namespace + +Tea.encrypt = function (plaintext, password) { + if (plaintext.length == 0) return (''); // nothing to encrypt + + // convert string to array of longs after converting any multi-byte chars to UTF-8 + var v = Tea.strToLongs(Utf8.encode(plaintext)); + if (v.length <= 1) v[1] = 0; // algorithm doesn't work for n<2 so fudge by adding a null + // simply convert first 16 chars of password as key + var k = Tea.strToLongs(Utf8.encode(password).slice(0, 16)); + var n = v.length; + + // ---- ---- + + var z = v[n - 1], y = v[0], delta = 0x9E3779B9; + var mx, e, q = Math.floor(6 + 52 / n), sum = 0; + + while (q-- > 0) { // 6 + 52/n operations gives between 6 & 32 mixes on each word + sum += delta; + e = sum >>> 2 & 3; + for (var p = 0; p < n; p++) { + y = v[(p + 1) % n]; + mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z); + z = v[p] += mx; + } + } + + // ---- ---- + + var ciphertext = Tea.longsToStr(v); + + return Base64.encode(ciphertext); +} + +/** @return {string} */ +Tea.decrypt = function (ciphertext, password) { + if (ciphertext.length == 0) return (''); + var v = Tea.strToLongs(Base64.decode(ciphertext)); + var k = Tea.strToLongs(Utf8.encode(password).slice(0, 16)); + var n = v.length; + + // ---- ---- + + var z = v[n - 1], y = v[0], delta = 0x9E3779B9; + var mx, e, q = Math.floor(6 + 52 / n), sum = q * delta; + + while (sum != 0) { + e = sum >>> 2 & 3; + for (var p = n - 1; p >= 0; p--) { + z = v[p > 0 ? p - 1 : n - 1]; + mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z); + y = v[p] -= mx; + } + sum -= delta; + } + + // ---- ---- + + var plaintext = Tea.longsToStr(v); + + // strip trailing null chars resulting from filling 4-char blocks: + plaintext = plaintext.replace(/\0+$/, ''); + + return Utf8.decode(plaintext); +} + +Tea.strToLongs = function (s) { // convert string to array of longs, each containing 4 chars + // note chars must be within ISO-8859-1 (with Unicode code-point < 256) to fit 4/long + var l = new Array(Math.ceil(s.length / 4)); + for (var i = 0; i < l.length; i++) { + // note little-endian encoding - endianness is irrelevant as long as + // it is the same in longsToStr() + l[i] = s.charCodeAt(i * 4) + (s.charCodeAt(i * 4 + 1) << 8) + + (s.charCodeAt(i * 4 + 2) << 16) + (s.charCodeAt(i * 4 + 3) << 24); + } + return l; // note running off the end of the string generates nulls since +} // bitwise operators treat NaN as 0 + +Tea.longsToStr = function (l) { // convert array of longs back to string + var a = new Array(l.length); + for (var i = 0; i < l.length; i++) { + a[i] = String.fromCharCode(l[i] & 0xFF, l[i] >>> 8 & 0xFF, + l[i] >>> 16 & 0xFF, l[i] >>> 24 & 0xFF); + } + return a.join(''); // use Array.join() rather than repeated string appends for efficiency in IE +} diff --git a/src/ext/utf8.js b/src/ext/utf8.js new file mode 100644 index 0000000..5b98238 --- /dev/null +++ b/src/ext/utf8.js @@ -0,0 +1,52 @@ +/* + * This is an early version of TEA Block: + * https://github.com/chrisveness/crypto/ + * (c) Chris Veness 2002-2017 + * + * MIT Licence + */ + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Utf8 class: encode / decode between multi-byte Unicode characters and UTF-8 multiple */ +/* single-byte character encoding (c) Chris Veness 2002-2012 */ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +var Utf8 = {}; // Utf8 namespace +Utf8.encode = function (strUni) { + // use regular expressions & String.replace callback function for better efficiency + // than procedural approaches + var strUtf = strUni.replace( + /[\u0080-\u07ff]/g, // U+0080 - U+07FF => 2 bytes 110yyyyy, 10zzzzzz + function (c) { + var cc = c.charCodeAt(0); + return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f); + } + ); + strUtf = strUtf.replace( + /[\u0800-\uffff]/g, // U+0800 - U+FFFF => 3 bytes 1110xxxx, 10yyyyyy, 10zzzzzz + function (c) { + var cc = c.charCodeAt(0); + return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3F, 0x80 | cc & 0x3f); + } + ); + return strUtf; +} +Utf8.decode = function (strUtf) { + // note: decode 3-byte chars first as decoded 2-byte strings could appear to be 3-byte char! + var strUni = strUtf.replace( + /[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars + function (c) { // (note parentheses for precence) + var cc = ((c.charCodeAt(0) & 0x0f) << 12) | ((c.charCodeAt(1) & 0x3f) << 6) | (c.charCodeAt(2) & 0x3f); + return String.fromCharCode(cc); + } + ); + strUni = strUni.replace( + /[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars + function (c) { // (note parentheses for precence) + var cc = (c.charCodeAt(0) & 0x1f) << 6 | c.charCodeAt(1) & 0x3f; + return String.fromCharCode(cc); + } + ); + return strUni; +} + diff --git a/src/helpers.js b/src/helpers.js new file mode 100644 index 0000000..115d5f9 --- /dev/null +++ b/src/helpers.js @@ -0,0 +1,151 @@ +/* + * helpers.js -- WME Validator helper functions + * Copyright (C) 2013-2018 Andriy Berestovskyy + * + * This file is part of WME Validator: https://github.com/WMEValidator/ + * + * WME Validator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * WME Validator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with WME Validator. If not, see . + */ + +/************************************************************************* + * HELPER FUNCTIONS + *************************************************************************/ + +/** + * Known class codes + */ +/** @const */ +var CC_UNDEFINED = 48; +/** @const */ +var CC_NULL = 34; +/** @const */ +var CC_BOOL = 46; +/** @const */ +var CC_NUMBER = 44; +/** @const */ +var CC_STRING = 58; +/** @const */ +var CC_GLOBAL = 5; +/** @const */ +var CC_FUNCTION = 37; +/** @const */ +var CC_ARRAY = 32; +/** @const */ +var CC_OBJECT = 42; +/** @const */ +var CC_REGEXP = 23; +/** @const */ +var CC_DATE = 33; + +/** + * Get object class name + */ +function classOf(val) { return {}.toString.call(val).slice(8, -1); } + +/** + * Get object class code + */ +function classCode(obj) +//{ return {}.toString.call(obj).charCodeAt(8) ^ {}.toString.call(obj).charCodeAt(11); } +{ + return CFCALLTHIS1(CF_CHARCODEAT, CFAPPLYTHIS(CF_TOSTRING, obj), 8) + ^ CFCALLTHIS1(CF_CHARCODEAT, CFAPPLYTHIS(CF_TOSTRING, obj), 11); +} + +/** + * Compare class code and object class code + */ +function classCodeIs(obj, cc) { + return cc === classCode(obj); +} + +/** + * Returns true if object is defined + */ +function classCodeDefined(obj) { + return CC_UNDEFINED !== classCode(obj); +} + +/** + * Check if object has no properties + */ +function isEmpty(obj) { + for (var k in obj) + if (obj.hasOwnProperty(k)) + return false; + return true; +} + +/** + * Deep copy of object or array + * See: object copy function at MDN + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach#An_object_copy_function + */ +function deepCopy(obj) { + switch (classCode(obj)) { + case CC_ARRAY: + var cpy = []; + for (var i = 0, len = obj.length; i < len; i++) + cpy[i] = deepCopy(obj[i]); + return cpy; + case CC_OBJECT: + var cpy = {}; + for (var attr in obj) + if (obj.hasOwnProperty(attr)) + cpy[attr] = deepCopy(obj[attr]); + return cpy; + } + return obj; +} + +/** + * Compare deep two arrays or objects + */ +function deepCompare(obj1, obj2) { + if (obj1 === obj2) + return true; + if (classCode(obj1) !== classCode(obj2)) + return false; + + switch (classCode(obj1)) { + case CC_ARRAY: + if (obj1.length != obj2.length) + return false; + for (var i = 0; i < obj1.length; i++) + if (!deepCompare(obj1[i], obj2[i])) + return false; + return true; + case CC_OBJECT: + for (var k in obj1) { + if (!obj1.hasOwnProperty(k)) + continue; + if (!obj2.hasOwnProperty(k)) + return false; + if (!deepCompare(obj1[k], obj2[k])) + return false; + } + return true; + } + // any other type + return false; +} + + +// array of critical functions +var CFA = ["length", [].unshift]; + +// add basic critical functions +CFADD({}.toString, "".charCodeAt); + +function CFEVAL1(cfunc, param1) { eval = CFGET(cfunc); return eval(param1) } diff --git a/src/lib/audio.js b/src/lib/audio.js new file mode 100644 index 0000000..5069d76 --- /dev/null +++ b/src/lib/audio.js @@ -0,0 +1,44 @@ +/* + * audio.js -- WME Validator audio context library + * Copyright (C) 2013-2018 Andriy Berestovskyy + * + * This file is part of WME Validator: https://github.com/WMEValidator/ + * + * WME Validator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * WME Validator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with WME Validator. If not, see . + */ + +/** + * Audio Context + * (c) 2013-2016 berestovskyy + * oscType: sine, square, sawtooth, triangle + */ +var _AUDIO = {}; +try { + _AUDIO._context = new (window.AudioContext || window.webkitAudioContext)(); + _AUDIO.beep = + /** @param {string=} oscType */ + function (dur, oscType) { + var osc = _AUDIO._context.createOscillator(); + osc.connect(_AUDIO._context.destination); + osc.type = oscType || "sine"; + osc.start(0); + setTimeout(function () { osc.stop(0) }, dur); + } +} +catch (e) { + _AUDIO._context = null; + _AUDIO.beep = + /** @param {string=} oscType */ + function (dur, oscType) { log("beep!") } +} diff --git a/src/lib/compressor.js b/src/lib/compressor.js new file mode 100644 index 0000000..eaedf5b --- /dev/null +++ b/src/lib/compressor.js @@ -0,0 +1,167 @@ +/* + * compressor.js -- WME Validator compressor library + * Copyright (C) 2013-2018 Andriy Berestovskyy + * + * This file is part of WME Validator: https://github.com/WMEValidator/ + * + * WME Validator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * WME Validator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with WME Validator. If not, see . + */ + +/** + * Compressor + * (c) 2013-2016 berestovskyy + */ +var _COMP = {}; + +/** + * Default dictionary + */ +//_COMP.$dict = [" segment",":{EN:\"","function","this.","\\u0"," the ","treet name","tion","roundabout","able:!1},","return","};v.b[",".length","\",FR:\"","\",PL:\"","~w0e","or Highway","~w5d","){var ",")&&f.d(","s://www.waz","\"http~ge.co","]={c:","~w1","drivable","Z(\"div\",",":{writ~r","\",a:\"","Validator","&&p.g(","ncorrect ","_Editing_","eating_and~","egment",";break;case","name",",e~zThe"," to ","+encodeURI(","Freeway","===","road",".match(/","~fm/forum/v","iewtopic.ph",".prototype."," at node ","\"},","if(","~e2,j:\"","roperties",".toUpperCas",".attributes","2952996808,"," conne","abbrevia~t","~w5e","ight",":{enumer~r","street_s~Zs","for","margin","estric~t"," not locked","ground-colo"," _COMP.$maxCode - _COMP.$minCode) + maxLen = _COMP.$maxCode - _COMP.$minCode; + + var top = []; + + // save dict + var dict = _COMP.$dict; + + var tstr = str; + while (top.length < num) { + var word = _COMP.getMostUsedWord(tstr, minLen, maxLen); + if (!word) break; + top.push(word); + // check dict size + if (top.join(" ").length > maxSize) { top.pop(); break; } + // compress the string + _COMP.setDictionary(top); + tstr = _COMP.compress(str); + }; + // restore the dictionary + _COMP.$dict = dict; + + return top; +} + +/** + * Get most used word in str + * with length between minLen and maxLen + */ +_COMP.getMostUsedWord = function (str, minLen, maxLen) { + var stat = {}; + var l = str.length; + for (var i = 0; i < l; i++) { + for (var j = minLen; j <= maxLen; j++) { + if (i + j >= l) + break; + var w = str.substr(i, j); + // check for special characters + if (/[\x00-\x1f]/.test(w)) + break; + if (stat[w]) + stat[w]++; + else + stat[w] = 1; + } + } + + // convert to an array of objects + var a = []; + for (var p in stat) { + if (stat.hasOwnProperty(p)) + a.push({ + text: p, + saved: stat[p] * (p.length - 2) + }); + } + if (!a.length) + return ""; + // sort the array + return a.sort(function (a, b) { + return b.saved - a.saved; + })[0].text; +} + +/** + * Set dictionary + */ +_COMP.setDictionary = function (dict) { + dict.length = Math.min(_COMP.$maxCode - _COMP.$minCode, dict.length); + _COMP.$dict = dict; + _COMP.$codes = []; +} + +/** + * Init codes for the dictionary + */ +_COMP.initCodes = function () { + if (_COMP.$codes.length != _COMP.$dict.length) + // init code array + for (var i = 0; i < _COMP.$dict.length; i++) + _COMP.$codes[i] = _COMP.$escChar + String.fromCharCode(_COMP.$maxCode - i); +} + +/** + * Compress the string + */ +_COMP.compress = function (str) { + _COMP.initCodes(); + + // escape escape char + str = str.split(_COMP.$escChar).join(_COMP.$escChar + _COMP.$escChar) + for (var i = 0; i < _COMP.$dict.length; i++) + str = str.split(_COMP.$dict[i]).join(_COMP.$codes[i]); + + return str; +} + +/** + * Decompress the string + */ +_COMP.decompress = function (str) { + _COMP.initCodes(); + + for (var i = _COMP.$dict.length - 1; i >= 0; i--) + str = str.split(_COMP.$codes[i]).join(_COMP.$dict[i]); + + // unescape escape char + str = str.split(_COMP.$escChar + _COMP.$escChar).join(_COMP.$escChar) + + return str; +} + +// critical functions in LIB +CFADD("log", eval, "error", "warning"); diff --git a/src/lib/i18n.js b/src/lib/i18n.js new file mode 100644 index 0000000..d5e4065 --- /dev/null +++ b/src/lib/i18n.js @@ -0,0 +1,314 @@ +/* + * i18n.js -- WME Validator internationalization library + * Copyright (C) 2013-2018 Andriy Berestovskyy + * + * This file is part of WME Validator: https://github.com/WMEValidator/ + * + * WME Validator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * WME Validator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with WME Validator. If not, see . + */ + +/** + * Simple I18n Class + * (c) 2013-2016 berestovskyy + * + * $defLng - default language + * $lng - current language + * $translations - translations database + * $curSet - current localization set (i.e. $translations[$lng]) + * $fallbackSet - fallback language set (i.e. $translations[$code2code] + * $defSet - default language set (i.e. $translations[$defLng] + * $code2code - fallback country code + * $lng2code - language to country translation table + * $code2dir - country code to direction translation table + */ +var _I18n = { + /** @const */ + $defLng: "EN", + // current language + $lng: "", + // localization database + $translations: null, + // current string set + $curSet: null, + // current code + $curCode: '', + // fallback string set + $fallbackSet: null, + // fallback code + $fallbackCode: '', + // default string set + $defSet: null, + // country <-> code conversion + $country2code: null, + $code2country: null, + // country fallback table + $code2code: null, + // language fallback table + $lng2code: null, + // rtl languages table + $code2dir: null +}; + +/** + * Init i18n + */ +_I18n.init = function (options) { + _I18n.$lng = options.$lng || _I18n.$defLng; + _I18n.$translations = options.$translations || {}; + _I18n.$country2code = options.$country2code || {}; + _I18n.$code2country = options.$code2country || {}; + _I18n.$code2code = options.$code2code || {}; + _I18n.$lng2code = options.$lng2code || {}; + _I18n.$code2dir = options.$code2dir || {}; +} + +/** + * Add translation + */ +_I18n.addTranslation = function (translation) { + var ccode = translation[".codeISO"]; + if (!ccode) { + // window.console.log("I18n: country code is not found"); + return; + } + ccode = ccode.toUpperCase(); + _I18n.$translations[ccode] = translation; + + if (_I18n.$defLng !== ccode) { + var country = translation[".country"]; + if (country) { + if (!classCodeIs(country, CC_ARRAY)) + country = [country]; + for (var i = 0; i < country.length; i++) { + var ucountry = country[i].toUpperCase(); + // if(ucountry in _I18n.$country2code) + // window.console.log("I18n: duplicate country: " + country[i]); + _I18n.$country2code[ucountry] = ccode; + if (!(ccode in _I18n.$code2country)) + _I18n.$code2country[ccode] = ucountry; + } + } + // else + // window.console.log("I18n: country is not found"); + + var lng = translation[".lng"]; + if (lng) { + if (!classCodeIs(lng, CC_ARRAY)) + lng = [lng]; + for (var i = 0; i < lng.length; i++) { + var ulng = lng[i].toUpperCase(); + // if(ulng in _I18n.$lng2code) + // window.console.log("I18n: duplicate language: " + lng[i]); + _I18n.$lng2code[ulng] = ccode; + } + } + var dir = translation[".dir"]; + if (dir) { + // if(ccode in _I18n.$code2dir) + // window.console.log("I18n: duplicate direction for code: " + ccode); + _I18n.$code2dir[ccode] = dir.toLowerCase(); + } + var fcode = translation[".fallbackCode"]; + if (fcode) { + fcode = fcode.toUpperCase(); + // if(ccode in _I18n.$code2code) + // window.console.log("I18n: duplicate fallback for code: " + ccode); + if (_I18n.$defLng !== fcode) + _I18n.$code2code[ccode] = fcode; + } + } + + _I18n.$curCode = _I18n.getCodeOL(_I18n.$translations, _I18n.$lng); + _I18n.$curSet = _I18n.getValueOC(_I18n.$translations, _I18n.$curCode); + + _I18n.$fallbackCode = _I18n.getFallbackCodeOC(_I18n.$translations, + _I18n.$curCode); + _I18n.$fallbackSet = _I18n.getValueOC(_I18n.$translations, + _I18n.$fallbackCode); + _I18n.$defSet = _I18n.$translations[_I18n.$defLng]; + + // window.console.log(_I18n) +} + +/** + * Get dependant codes + */ +_I18n.getDependantCodes = function (uc) { + var ret = []; + for (var depCode in _I18n.$code2code) { + if (_I18n.$code2code[depCode] === uc) + ret.push(depCode); + } + return ret; +} + +/** + * Get country code + */ +_I18n.getCountryCode = function (uc) { + if (uc in _I18n.$country2code) + return _I18n.$country2code[uc]; + return ""; +} + +/** + * Get country + */ +_I18n.getCountry = function (ucc) { + if (ucc in _I18n.$code2country) + return _I18n.$code2country[ucc]; + return ""; +} + +/** + * Get capitalized country + */ +_I18n.getCapitalizedCountry = function (ucc) { + return _I18n.capitalize(_I18n.getCountry(ucc)).toLowerCase().replace(/\b./g, function (c) { + return c.toUpperCase(); + }); +} + +/** + * Capitalize country + */ +_I18n.capitalize = function (str) { + return str.toLowerCase().replace(/\b./g, function (c) { + return c.toUpperCase(); + }); +} + +/** + * Get direction + */ +_I18n.getDir = function () { + if (_I18n.$curCode in _I18n.$code2dir) + return _I18n.$code2dir[_I18n.$curCode]; + if (_I18n.$fallbackCode in _I18n.$code2dir) + return _I18n.$code2dir[_I18n.$fallbackCode]; + return "ltr"; +} + +/** + * Get string from the translations + */ +_I18n.getString = function (label) { + if (label in _I18n.$curSet) + // return "ok" + _I18n.$curSet[label]; + return _I18n.$curSet[label]; + if (label in _I18n.$fallbackSet) + // return "ok" + _I18n.$fallbackSet[label]; + return _I18n.$fallbackSet[label]; + if (label in _I18n.$defSet) + return _I18n.$defSet[label]; + // TODO: remove + var ret = "[missing " + label + "]"; + // window.console.log(ret); + return ret; +} + +/** + * Check if label exists + */ +_I18n.isLabelExist = function (label) { + if (label in _I18n.$curSet) + return true; + if (label in _I18n.$fallbackSet) + return true; + if (label in _I18n.$defSet) + return true; + return false; +} + +/** + * Get country code from object by language + */ +_I18n.getCodeOL = function (obj, lng) { + var ccode = _I18n.$lng2code[lng]; + if (ccode) { + if (ccode in obj) + return ccode; + else + return _I18n.getFallbackCodeOC(obj, ccode); + } + else + return _I18n.$defLng; +} + +/** + * Get fallback country code from object by code + */ +_I18n.getFallbackCodeOC = function (obj, ccode) { + var fcode = _I18n.$code2code[ccode]; + if (fcode && (fcode in obj)) + return fcode; + return _I18n.$defLng; +} + + + +/** + * Get value from object by language + */ +_I18n.getValueOL = function (obj, lng) { + return _I18n.getValueOC(obj, _I18n.getCodeOL(obj, lng)); +} + +/** + * Get value by object and country code + */ +_I18n.getValueOC = function (obj, ccode) { + if (ccode in obj) + return obj[ccode]; + else { + if (ccode in _I18n.$code2code) { + var fcode = _I18n.$code2code[ccode]; + if (fcode in obj) + return obj[fcode]; + } + } + return obj[_I18n.$defLng]; +} + +/** + * Expand string with options object + */ +_I18n.expandSO = function (str, options) { + if (!options) return str; + + return str.replace(/\$\{(\w+)(\[(\d+)\]|\[(\W*)\])?\}/g, + function (all, name, arr, idx, delims) { + if (arr) { + if (idx) + return options[name][idx] || ''; + return options[name].join(delims); + } + else + return options[name]; + }); +} + +/** + * Translate Object + */ +_I18n.t = function (obj, ccode, options) { + return _I18n.expandSO(_I18n.getValueOC(obj, ccode), options); +} + +/** + * Translate Object using language + */ +_I18n.tL = function (obj, options) { + return _I18n.expandSO(_I18n.getValueOL(obj, _I18n.$lng), options); +} diff --git a/src/lib/thui.js b/src/lib/thui.js new file mode 100644 index 0000000..cd8be8f --- /dev/null +++ b/src/lib/thui.js @@ -0,0 +1,654 @@ +/* + * thui.js -- WME Validator tiny HTML user interface library + * Copyright (C) 2013-2018 Andriy Berestovskyy + * + * This file is part of WME Validator: https://github.com/WMEValidator/ + * + * WME Validator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * WME Validator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with WME Validator. If not, see . + */ + + +/** + * Tiny HTML User Interface + * see header for THUI types declarations + * (c) 2013-2016 berestovskyy + */ + +/** + * Default parameters for children objects + */ +/** @struct */ +_THUI.$def = { + _class: "", + _disclose: 0, + _name: "", + _nodisplay: 0, + _disabled: 0, + _reverse: 0, + _style: "", + _type: "", + _onclick: null, + _onwarning: null, + _onchange: null +} + +/** + * Load values from storage object + */ +_THUI.loadValues = function (uiObj, storageObj) { + if (!storageObj) return; + + if (uiObj.AUTOSAVE && (uiObj.AUTOSAVE in storageObj)) { + switch (uiObj.TYPE) { + case _THUI.TEXT: + case _THUI.DATE: + uiObj.VALUE = storageObj[uiObj.AUTOSAVE]; + break; + default: + uiObj.CHECKED = storageObj[uiObj.AUTOSAVE]; + break; + } + return; + } + + for (var i in uiObj) { + if (!uiObj.hasOwnProperty(i)) + continue; + + var o = uiObj[i]; + switch (classCode(o)) { + case CC_OBJECT: + _THUI.loadValues(o, storageObj); + break; + case CC_ARRAY: + for (var j = 0; j < o.length; j++) + _THUI.loadValues(o[j], storageObj); + break; + } + } +} + +/** + * Save all values to storage object + * @param {Object=} storageObj + */ +_THUI.saveValues = function (uiObj, storageObj) { + if (!storageObj) storageObj = {}; + + if (uiObj.AUTOSAVE) { + switch (uiObj.TYPE) { + case _THUI.TEXT: + case _THUI.DATE: + storageObj[uiObj.AUTOSAVE] = uiObj.VALUE; + break; + default: + storageObj[uiObj.AUTOSAVE] = uiObj.CHECKED; + break; + } + return storageObj; + } + + for (var i in uiObj) { + if (!uiObj.hasOwnProperty(i)) + continue; + + var o = uiObj[i]; + switch (classCode(o)) { + case CC_OBJECT: + _THUI.saveValues(o, storageObj); + break; + case CC_ARRAY: + for (var j = 0; j < o.length; j++) + _THUI.saveValues(o[j], storageObj); + break; + } + } + return storageObj; +} + +/** + * Local storage wrappers + */ +_THUI.storage = { + get: function (name) { + try { + var s = window.localStorage.getItem(name); + return s ? JSON.parse(Base64.decode(s)) : null; + } + catch (e) { + return null; + } + }, + set: function (name, obj) { + try { + var s = JSON.stringify(obj); + window.localStorage.setItem(name, Base64.encode(s)); + return true; + } + catch (e) { + return false; + } + } +}; + +/** + * Add an element class style to the document + */ +_THUI.addElemetClassStyle = function (elem, cl, newStyle) { + if (classCodeIs(cl, CC_NUMBER)) cl = "c" + cl; + return _THUI.addStyle(elem + "." + cl + newStyle); +} + +/** + * Add an element ID style to the document + */ +_THUI.addElemetIdStyle = function (elem, id, newStyle) { + if (classCodeIs(id, CC_NUMBER)) id = "i" + id; + return _THUI.addStyle(elem + "#" + id + newStyle); +} + +/** + * Add a style to the document + */ +_THUI.addStyle = function (newStyle) { + // we assume a style sheet is already exists + for (var i = 0; i < 10; i++) { + var sheet = document.styleSheets[i]; + try { + if ("cssRules" in sheet) { + return sheet.insertRule(newStyle, sheet.cssRules.length); + } + } catch (e) { }; + } +} + +/** + * Get UI element by DOM element + */ +_THUI.getByDOM = function (uiObj, elem) { + if (uiObj.IDOM == elem || uiObj.ODOM == elem) + return uiObj; + + var ret = null; + for (var i in uiObj) { + if (!uiObj.hasOwnProperty(i)) + continue; + + var o = uiObj[i]; + switch (classCode(o)) { + case CC_OBJECT: + if (ret = _THUI.getByDOM(o, elem)) + return ret; + break; + case CC_ARRAY: + for (var j = 0; j < o.length; j++) + if (ret = _THUI.getByDOM(o[j], elem)) + return ret; + break; + } + } + return null; +} + +/** + * Get element by ID + */ +_THUI.getById = function (uiObj, id) { + if (uiObj.ID && uiObj.ID == id) + return uiObj; + + var ret = null; + for (var i in uiObj) { + if (!uiObj.hasOwnProperty(i)) + continue; + + var o = uiObj[i]; + switch (classCode(o)) { + case CC_OBJECT: + if (ret = _THUI.getById(o, id)) + return ret; + break; + case CC_ARRAY: + for (var j = 0; j < o.length; j++) + if (ret = _THUI.getById(o[j], id)) + return ret; + break; + } + } + return null; +} + +/** + * Update view + */ +_THUI.docToView = function (uiObj) { + // initiate update mode by passing null parent + _THUI.appendUI(null, uiObj); +} + +/** + * Update document + * + * Updates only: VALUE, CHECKED + */ +_THUI.viewToDoc = function (uiObj) { + if (uiObj.IDOM) { + if (classCodeDefined(uiObj.IDOM.value)) { + var val = uiObj.IDOM.value; + if (classCodeDefined(uiObj.MAX) && val > uiObj.MAX) + val = uiObj.MAX; + if (classCodeDefined(uiObj.MIN) && val < uiObj.MIN) + val = uiObj.MIN; + uiObj.VALUE = val; + } + if (classCodeDefined(uiObj.IDOM.checked)) + uiObj.CHECKED = uiObj.IDOM.checked; + } + + for (var i in uiObj) { + if (!uiObj.hasOwnProperty(i)) + continue; + + var o = uiObj[i]; + switch (classCode(o)) { + case CC_OBJECT: + _THUI.viewToDoc(o); + break; + case CC_ARRAY: + o.forEach(_THUI.viewToDoc); + break; + } + } +} + +/** + * Creates or updates user interface + * + * Just update values if the parent parameter is null + * + * About _THIU + * All simple values are properties + * All structures and arrays are sub-elements/lists + * + * Prefixes: + * _ - use _ prefix to set a default parameter for children objects + * + * For the first element we use: + * [_]CLASS - element class + * [_]STYLE[IO] - direct styles for inner/outer elements + * + * For the inner (input) element we use: + * CHECKED - the element is checked by default + * [_]DISABLED - the element is disabled by default + * MAX - max value + * MAXLENGTH - max length + * MIN - min value + * [_]NAME - the name of an element + * READONLY - the element is readonly + * STEP - step value + * VALUE - default value + * ONCLICK - on click handler + * + * For both elements we use: + * [_]NODISPLAY - the element is hidden by default + * TITLE - element title text + * + * Special: + * ID - assign an id to the element (for inner if present, otherwise for outer) + * [_]DISCLOSE - disclose inner element from outer + * [_]REVERSE - reverse order of elements + * TEXT - main element text (depend on type) + * [_]TYPE - element type: + * DIV - simple div element + * NUMBER - input + label + * RADIO - input + label + * CHECKBOX - input + label + * + * What is DISCLOSE? + * Some of the objects consists 3 elements: outer, inner and outer text. + * Standard way of representing them is: outer text + * If DISCLOSE is present: outer text + * + * What is REVERSE? + * RVERSE changes inner element and outer text: + * outer text --> outer text + * If DISCLOSE is present it reverse order of elements: + * outer text --> outer text + */ + +/** + * Append user interface object representation as a child to the given + * parent element + * + * @param {?HTMLElement} parent + * @param {Object} uiObj + * @param {string=} uiPrefix + * @param {string=} uiName + */ +_THUI.appendUI = function (parent, uiObj, uiPrefix, uiName) { + // check the arguments + uiPrefix = uiPrefix || ""; + uiName = uiName || ""; + + // get the properties + var id = uiObj.ID; + if (!classCodeDefined(id)) id = ""; + /** @const */ + var NA = uiObj.NA || false; + /** @const */ + var NAti = uiObj.NATITLE || ""; + /** @const */ + var ch = uiObj.CHECKED || false; + var cl = classCodeDefined(uiObj.CLASS) ? uiObj.CLASS : _THUI.$def._class; + var cli = uiObj.CLASSI; + /** @const */ + var _cl = uiObj._CLASS; + /** @const */ + var va = uiObj.VALUE; + /** @const */ + var disc = classCodeDefined(uiObj.DISCLOSE) ? uiObj.DISCLOSE : _THUI.$def._disclose; + /** @const */ + var _disc = uiObj._DISCLOSE; + /** @const */ + // NA element always disabled + var di = NA ? NA : (classCodeDefined(uiObj.DISABLED) ? uiObj.DISABLED : _THUI.$def._disabled); + /** @const */ + var _di = uiObj._DISABLED; + /** @const */ + var no = classCodeDefined(uiObj.NODISPLAY) ? uiObj.NODISPLAY : _THUI.$def._nodisplay; + /** @const */ + var _no = uiObj._NODISPLAY; + /** @const */ + var ma = uiObj.MAX; + /** @const */ + var mal = uiObj.MAXLENGTH; + /** @const */ + var plh = uiObj.PLACEHOLDER; + /** @const */ + var mi = uiObj.MIN; + var name = classCodeDefined(uiObj.NAME) ? uiObj.NAME : _THUI.$def._name; + /** @const */ + var _name = uiObj._NAME; + /** @const */ + var ro = uiObj.READONLY || false; + /** @const */ + var re = classCodeDefined(uiObj.REVERSE) ? uiObj.REVERSE : _THUI.$def._reverse; + /** @const */ + var _re = uiObj._REVERSE; + /** @const */ + var step = uiObj.STEP; + /** @const */ + var st = classCodeDefined(uiObj.STYLE) ? uiObj.STYLE : _THUI.$def._style; + /** @const */ + var _st = uiObj._STYLE; + /** @const */ + var sti = classCodeDefined(uiObj.STYLEI) ? uiObj.STYLEI : ""; + /** @const */ + var sto = classCodeDefined(uiObj.STYLEO) ? uiObj.STYLEO : ""; + /** @const */ + var te = uiObj.TEXT || ""; + // NA element has different title + /** @const */ + var ti = NA ? (NAti ? NAti : "Not available") : uiObj.TITLE; + /** @const */ + var ty = classCodeDefined(uiObj.TYPE) ? uiObj.TYPE : _THUI.$def._type; + /** @const */ + var _ty = uiObj._TYPE; + /** @const */ + var accK = uiObj.ACCESSKEY || ""; + + // handlers + /** @const */ + var oncl = uiObj.ONCLICK || _THUI.$def._onclick; + /** @const */ + var onclo = uiObj.ONCLICKO; + /** @const */ + var _oncl = uiObj._ONCLICK; + /** @const */ + var onwa = uiObj.ONWARNING || _THUI.$def._onwarning; + /** @const */ + var _onwa = uiObj._ONWARNING; + /** @const */ + var onch = uiObj.ONCHANGE || _THUI.$def._onchange; + /** @const */ + var _onch = uiObj._ONCHANGE; + + // array of elements to push + var els = []; + // inner element by defauld is input + var iel = document.createElement("input"); + // outer element (i.e. ) + // by default is label + var oel = document.createElement("label"); + // outer text (i.e. ) + // by default is TEXT + var ote = te; + + // check and convert types + if (classCodeIs(uiPrefix, CC_NUMBER)) uiPrefix = "p" + uiPrefix; + if (classCodeIs(id, CC_NUMBER)) id = "i" + id; + if (classCodeIs(cl, CC_NUMBER)) cl = "c" + cl; + if (classCodeIs(cli, CC_NUMBER)) cli = "c" + cli; + if (classCodeIs(name, CC_NUMBER)) name = "n" + name; + + // create new elements + switch (ty) { + case _THUI.NONE: + iel = oel = null; + ote = ""; + break; + case _THUI.NUMBER: + iel.type = "number"; + break; + case _THUI.RADIO: + // we need an unique id to disclose input from label + if (disc && !id) id = uiPrefix + uiName + "i"; + // name should be the same across radio buttons + if (!name) name = uiPrefix + "n"; + + iel.type = "radio"; + if (disc) oel.htmlFor = id; + break; + case _THUI.CHECKBOX: + // we need an unique id to disclose input from label + if (disc && !id) id = uiPrefix + uiName + "i"; + // name should be unique as well + //if(!name) name = uiPrefix + uiName + "n"; + + iel.type = "checkbox"; + if (disc) oel.htmlFor = id; + break; + case _THUI.BUTTON: + iel = document.createElement("button"); + // if(te) iel.appendChild(document.createTextNode(te)); + if (te) iel.innerHTML = te; + oel = null; + ote = ""; + break; + case _THUI.TEXT: + iel.type = "text"; + break; + case _THUI.PASSWORD: + iel.type = "password"; + break; + case _THUI.DATE: + iel.type = "date"; + break; + default: + // create simple div element with text inside by default + iel = null; + oel = document.createElement("div"); + break; + } + // combine elements + if (oel && iel && !disc) { + if (re) + // enclosed reverse composition: outer text + oel.appendChild(iel); + else + // [default] enclosed composition: outer text + oel.insertBefore(iel, oel.firstChild); + } + + // now check if the elements are new + if (classCodeDefined(uiObj.ODOM)) + // we already have ODOM, so just get them + oel = uiObj.ODOM; + if (classCodeDefined(uiObj.IDOM)) + // we already have IDOM, so just get them + iel = uiObj.IDOM; + + // add text to the outer element if present + if (ote) { + var spanEl = document.createElement("span"); + spanEl.innerHTML = ote; + // to switch on/off validator? + spanEl.style.pointerEvents = "none"; + // remove all spans + var oldSpans = oel.getElementsByTagName("span"); + var bInserted = false; + for (var i = 0; i < oldSpans.length; i++) + oel.removeChild(oldSpans[i]); + + oel.insertBefore(spanEl, oel.firstChild); + } + + // push elements + if (oel && iel) { + // only if we have both inner and outer elements + if (disc) { + if (re) + // disclosed reverse composition: outer text + els.push(oel, iel); + else + // disclosed composition: outer text + els.push(iel, oel); + } + else + els.push(oel); + } + else { + // we have just one or no elements + if (oel) els.push(oel); + if (iel) els.push(iel); + } + + // ID is for inner if present, otherwise for outer + if (id) { + if (iel) iel.id = id; + else if (oel) oel.id = id; + + // store id for futher use + uiObj.ID = id; + } + + // store name for future use + if (name) + uiObj.NAME = name; + + // add attributes to the inner element + if (iel) { + if (cli) iel.className = cli; + if (accK) iel.accessKey = accK; + if (classCodeDefined(ch)) iel.checked = ch; + iel.disabled = di; + if (classCodeDefined(ma)) iel.max = ma; + if (classCodeDefined(mal)) iel.maxLength = mal; + if (classCodeDefined(mi)) iel.min = mi; + if (plh) iel.placeholder = plh; + if (name) iel.name = name; + if (classCodeDefined(ro)) iel.readonly = ro; + if (classCodeDefined(step)) iel.step = step; + if (classCodeDefined(va)) iel.value = va; + + if (classCodeDefined(oncl)) iel.onclick = oncl; + if (classCodeDefined(onch)) iel.onchange = onch; + if (classCodeDefined(onwa) + && uiObj.WARNING) iel.onchange = onwa; + if (sti) iel.style.cssText = sti; + } + + // add attributes to the outer element + if (oel) { + if (classCodeDefined(onclo)) oel.onclick = onclo; + if (sto) oel.style.cssText = sto; + } + + // add attributes to the first element + var fel = els[0]; + if (fel) { + if (cl) fel.className = cl; + if (st) fel.style.cssText = st; + } + else + // if no elements - append subobjects directly to the parent + fel = parent; + + // append all sub-objects + // save the defaults + var oldDef = deepCopy(_THUI.$def); + + // set new defaults + if (classCodeDefined(_cl)) _THUI.$def._class = _cl; + if (classCodeDefined(_disc)) _THUI.$def._disclose = _disc; + if (classCodeDefined(_name)) _THUI.$def._name = _name; + if (classCodeDefined(_di)) _THUI.$def._disabled = _di; + if (classCodeDefined(_no)) _THUI.$def._nodisplay = _no; + if (classCodeDefined(_re)) _THUI.$def._reverse = _re; + if (classCodeDefined(_st)) _THUI.$def._style = _st; + if (classCodeDefined(_ty)) _THUI.$def._type = _ty; + if (_oncl) _THUI.$def._onclick = _oncl; + if (_onch) _THUI.$def._onchange = _onch; + if (_onwa) _THUI.$def._onwarning = _onwa; + + for (var i in uiObj) { + if (!uiObj.hasOwnProperty(i)) + continue; + + var o = uiObj[i]; + + switch (classCode(o)) { + case CC_OBJECT: + fel = _THUI.appendUI(fel, o, uiPrefix + uiName, i); + break; + case CC_ARRAY: + for (var j = 0; j < o.length; j++) + if (classCodeIs(o[j], CC_OBJECT)) + fel = _THUI.appendUI(fel, o[j], uiPrefix + uiName, i); + break; + } + } + // restore the defaults + _THUI.$def = oldDef; + + // add attributes to both elements and add it to the parent + els.forEach(function (e) { + if (no) e.style.display = "none"; else e.style.display = ""; + if (classCodeDefined(ti)) e.title = ti; + + if (e !== uiObj.IDOM && e !== uiObj.ODOM) + parent.appendChild(e); + }); + + // store elements for futher updates + uiObj.IDOM = iel; + uiObj.ODOM = oel; + // make IDOM/ODOM hidden for iterations + Object.defineProperties(uiObj, { + IDOM: { enumerable: false }, + ODOM: { enumerable: false } + }); + + return parent; +} diff --git a/src/login.js b/src/login.js new file mode 100644 index 0000000..1e21961 --- /dev/null +++ b/src/login.js @@ -0,0 +1,1299 @@ +/* + * login.js -- WME Validator initialization on user login + * Copyright (C) 2013-2018 Andriy Berestovskyy + * + * This file is part of WME Validator: https://github.com/WMEValidator/ + * + * WME Validator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * WME Validator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with WME Validator. If not, see . + */ + +/************************************************************************* + * ENCRYPTED FUNCTIONS + *************************************************************************/ + +/** + * Login new user + */ +_WV.$functions[F_LOGIN] = function() +{ + log("login " + WLM.user.userName); + + /////////////////////////////////////////////////////////////////////// + // Support Functions + /** + * Parse access list and returns array of rules + */ + _WV.parseAccessList = function(s) + { + var a = s.split(/\s*,\s*/) + var res = []; + a.forEach(function(r, i) { + var n = false; + if("!" === r.charAt(0)) + n = true, r = r.slice(1); + res[i] = {$id: r, $not: n} + }); + return res; + } + + /** + * Check for list of rules + */ + _WV.checkAccessFor = function(forStr, cmpFunc) + { + // empty list is true + if(!forStr) + return true; + var l = _WV.parseAccessList(forStr); + // empty list is true + if(!l.length) + return true; + for(var i = 0; i < l.length; i++) + { + var r = l[i]; + if("*" === r.$id || cmpFunc(r.$id)) + { + // match found + if(r.$not) + return false; + else + return true; + } + } + // non-empty list did not match - false + return false; + } + // Generate mirror checks + function mirrorChecks(defTranslation) + { + var allLabels = _RT.$otherLabels.concat(_RT.$textLabels); + for(var i = CK_MIRRORFIRST; i <= CK_MIRRORLAST; i++) + { + allLabels.forEach(function(l) { + var label = i + '.' + l; + if(!(label in defTranslation)) return; + + var value = defTranslation[label]; + var mLabel = (i + 100) + '.' + l; + switch(l) + { + case "title": + case "problem": + case "solution": + defTranslation[mLabel] = value.replace(/ A($|\b)/g, " B"); + break; + case "params": + defTranslation[mLabel] = deepCopy(value); + break; + default: + defTranslation[mLabel] = value; + break; + } + }); + } + } // mirorChecks + + + /////////////////////////////////////////////////////////////////////// + // Runtime private variables + /** @struct */ + _RT = { + // package labels + $textLabels: ['title', 'problem', 'solution'], + $otherLabels: ['enabled', 'color', 'severity', + 'reportOnly', 'params', 'problemLink', 'solutionLink'], + // max severity possible + $curMaxSeverity: RS_ERROR, + // saved options + $RegExp1: '', + $RegExp2: '', + // hidden options + oReportWMECH: { + FORID: '_cbHighlightLocked', + CHECKED: false, + NA: true, + }, + oReportToolbox: { + FORID: 'WMETB_NavBar', + CHECKED: false, + NA: true, + }, + + // map changed flag + $isMapChanged: false, + // current language + $lng: I18n.locale.toUpperCase(), + // cache for updated by filter + $includeUpdatedByCache: {}, + // filter parsed time + $includeUpdatedSinceTime: 0, + // cache for city name filter + $includeCityNameCache: {}, + // cache for checks filter + $includeChecksCache: {}, + // switch Validator on/off + $switchValidator: false, + // HL layer + $HLlayer: null, + // array of objects to animate + $HLedObjects: {}, + // global access flag + $isGlobalAccess: false, + // timer to measure ETA + $timer: { $secInRun: 0, $lastUpdate: 0 }, + // current message to show + $curMessage: {TEXT: "", TITLE: "", CLASS: CL_MSG}, + // top city and country + $topCity: null, + // cached top country code + $cachedTopCCode: "", + // top (logged in) user + $topUser: { + $userID: WLM.user.id, + $userName: WLM.user.userName, + $userLevel: WLM.user.normalizedLevel, + $isSuperUser: WLM.user.userName === GA_SUPERUSER, + $isCM: WLM.user.editableCountryIDs ? + 0 !== WLM.user.editableCountryIDs.length : false, + $countryIDs: WLM.user.editableCountryIDs ? + WLM.user.editableCountryIDs : [], + }, + // top (current) map center + $topCenter: null, + // watch dog + $WDmoveID: -1, + $WDloadID: -1, + // current layers visibility + $layersVisibility: "", + // current state + $state: ST_STOP, + // current direction + $direction: DIR_L2R, + // first step + $firstStep: true, + // current map position + $startExtent: null, + $startCenter: null, + $startZoom: null, + $nextCenter: null, + $moveEndCenter: null, + // map of seen object IDs + $seen: {}, + // map of segment IDs to revalidate + $revalidate: {}, + // hax message number + $haxMessage: 0, + // current user + $curUserName: WLM.user.userName, + // error flag + $error: false, + // no editable segment was found - show a message + $reportEditableNotFound: false, + // unsorted checks + $checks: {}, + // sorted checks + $sortedCheckIDs: null, + // WMECH colors + $WMECHcolors: {}, + // untranslated languages +// $untranslatedLocales: ["ja", "ro", "tr", "af", +// "ko", "ms", "hu", "nl", "sv", "no", +// "da", "lt", "zh"], + $untranslatedLngs: ["IT"], + }; + + /////////////////////////////////////////////////////////////////////// + // WV Checks + /////////////////////////////////////////////////////////////////////// + _RT.$checks = { + // Global access + 0: { + SEVERITY: RS_MAX, + REPORTONLY: false, + TITLE: 'Global access list to test before any of the checks below', + FORCOUNTRY: GA_FORCOUNTRY, + FORCITY: GA_FORCITY, + FORUSER: GA_FORUSER, + FORLEVEL: GA_FORLEVEL, + OPTIONS: {}, + COLOR: '', + PROBLEM: '', + PROBLEMLINK: '', + PROBLEMLINKTEXT: '', + SOLUTION: '', + SOLUTIONLINK: '', + SOLUTIONLINKTEXT: '', + }, + }; // _RT.$checks + + /////////////////////////////////////////////////////////////////////// + // Locales + /////////////////////////////////////////////////////////////////////// + // init I18n + _I18n.init({$lng: _RT.$lng}); + + /** @const */ + var defTranslation = _translations[_I18n.$defLng]; + + // Generate Toolbox check descriptions in EN + /** @const */ + var defTBProblem = "The segment is highlighted by WME Toolbox. It is not a problem"; + /** @const */ + var defTBProblemLink = "W:Community_Plugins,_Extensions_and_Tools#WME_Toolbox"; + /** @const */ + var TBchecks = [ + // color, severity, reportOnly, title, + // problem, problemLink, + // solution, solutionLink + ["#3030FF", 'W', , "Roundabout which may cause issues", + "Junction IDs of the roundabout segments are not consecutive", "", + "Redo the roundabout", "W:Creating_and_Editing_a_roundabout#Improving_manually_drawn_roundabouts" + ], + ["#FF30FF", , , "Simple segment", + "The segment has unneeded geometry nodes", , + "Simplify segment geometry by hovering mouse pointer and pressing \"d\" key", + "W:Creating_and_Editing_street_segments#Adjusting_road_geometry_.28nodes.29" + ], + ["#11F247", , true, "Lvl 2 lock"], + ["#71F211", , true, "Lvl 3 lock"], + ["#E2F211", , true, "Lvl 4 lock"], + ["#F29011", , true, "Lvl 5 lock"], + ["#F22011", , true, "Lvl 6 lock"], + ["#00A8FF", , true, "House numbers"], + ["#F7B020", , true, "Segment with time restrictions"] + ]; + for(var i = CK_TBFIRST; i <= CK_TBLAST; i++) + { + var cc = TBchecks[i - CK_TBFIRST]; + var cp = cc[4] || defTBProblem; + var cpl = cc[5]; + if(!classCodeDefined(cpl)) + cpl = defTBProblemLink; + + defTranslation[i + '.enabled'] = true; + defTranslation[i + '.color'] = cc[0]; + if(cc[1]) defTranslation[i + '.severity'] = cc[1]; + if(cc[2]) defTranslation[i + '.reportOnly'] = cc[2]; + defTranslation[i + '.title'] = "WME Toolbox: " + cc[3]; + defTranslation[i + '.problem'] = cp; + if(cpl) defTranslation[i + '.problemLink'] = cpl; + if(cc[6]) defTranslation[i + '.solution'] = cc[6]; + if(cc[7]) defTranslation[i + '.solutionLink'] = cc[7]; + } + + // Generate WMECH check descriptions in EN + /** @const */ + var defWMECHProblem = "The segment is highlighted by WME Color Highlights. It is not a problem"; + /** @const */ + var defWMECHProblemLink = "W:Community_Plugins,_Extensions_and_Tools#WME_Color_Highlights_.28WMECH.29"; + /** @const */ + var WMECHchecks = [ + // color, severity, reportOnly, title, + // problem, problemLink, + // solution, solutionLink + ["#000000", , true, "Editor lock"], + ["#0000FF", , true, "Toll road / One way road"], + ["#00FF00", , true, "Recently edited"], + ["#880000", , true, "Road rank"], + ["#888888", , true, "No city"], + ["#990099", , true, "Time restriction / Highlighted road type"], + ["#FFBB00", , true, "No name"], + ["#FFFF00", , true, "Filter by city"], + ["#FFFF01", , true, "Filter by city (alt. city)"], + ["#00FF00", , true, "Filter by editor"] + ]; + for(var i = CK_WMECHFIRST; i <= CK_WMECHLAST; i++) + { + var cc = WMECHchecks[i - CK_WMECHFIRST]; + /** @const */ + var cp = defWMECHProblem; + /** @const */ + var cpl = defWMECHProblemLink; + + defTranslation[i + '.enabled'] = true; + defTranslation[i + '.color'] = cc[0]; + if(cc[1]) defTranslation[i + '.severity'] = cc[1]; + if(cc[2]) defTranslation[i + '.reportOnly'] = cc[2]; + defTranslation[i + '.title'] = "WME Color Highlights: " + cc[3]; + defTranslation[i + '.problem'] = cp; + if(cpl) defTranslation[i + '.problemLink'] = cpl; + } + + /** @const */ + var streetNames = ["Freeway", "Major Highway", "Minor Highway", + "Ramp", "Primary Street", "Street", "Parking Lot Road", + "Railroad"]; + // Generate custom checks descriptions in EN + for(var i = CK_TYPEFIRST; i <= CK_TYPELAST; i++) + { + var streetName = streetNames[i - CK_TYPEFIRST]; + defTranslation[i + '.severity'] = "W"; + defTranslation[i + '.title'] = "Must be a " + streetName; + defTranslation[i + '.problem'] = "This segment must be a " + streetName; + defTranslation[i + '.solution'] = "Set the road type to " + + streetName + " or change the road name"; + } + // Generate custom checks descriptions in EN + for(var i = CK_CUSTOMFIRST; i <= CK_CUSTOMLAST; i++) + { + defTranslation[i + '.title'] = "Custom check"; + defTranslation[i + '.severity'] = "W"; + defTranslation[i + '.problem'] = "The segment matched custom conditions"; + defTranslation[i + '.solution'] = "Solve the issue"; + defTranslation[i + '.params'] = { + "template.title": "{string} expandable template", + "template": "${street}", + "regexp.title": "{string} regular expression to match the template", + "regexp": "!/.+/", + "titleEN.title": "{string} check title in English", + "titleEN": "", + "problemEN.title": "{string} problem description in English", + "problemEN": "", + "solutionEN.title": "{string} solution instructions in English", + "solutionEN": "", + }; + } + // Generate lock checks descriptions in EN + /** @const */ + var lockLevels = { + 150: 5, + 151: 4, + 152: 3, + 153: 4, + 154: 2, + }; + for(var i = CK_LOCKFIRST; i <= CK_LOCKLAST; i++) + { + var lockName = streetNames[i - CK_LOCKFIRST]; + var lockLevel = lockLevels[i]; + defTranslation[i + '.title'] = "No lock on " + lockName; + defTranslation[i + '.problem'] = "The " + lockName + " segment should be locked at least to Lvl ${n}"; + defTranslation[i + '.solution'] = "Lock the segment"; + defTranslation[i + '.params'] = { + "n.title": "{number} minimum lock level", + "n": lockLevel, + }; + } + // Generate street type-names + /** @const */ + var streetRegExps = { + 160: "!/^[AS][0-9]{1,2}/", + 161: "!/^[0-9]{1,2}/", + 162: "!/^[0-9]{1,3}/", + 163: "!/^[AS]?[0-9]* ?> /", + }; + /** @const */ + var streetDefRegExp = "!/.?/"; + for(var i = CK_STREETTNFIRST; i <= CK_STREETTNLAST; i++) + { + var streetName = streetNames[i - CK_STREETTNFIRST]; + var streetRegExp = streetRegExps[CK_STREETTNFIRST] || streetDefRegExp; + if(i < 165 || i > 167) + defTranslation[i + '.severity'] = "W"; + defTranslation[i + '.title'] = "Incorrect " + streetName + " name"; + defTranslation[i + '.problem'] = "The " + streetName + + " segment has incorrect street name"; + defTranslation[i + '.solution'] = "Rename the segment in accordance with the guidelines"; + defTranslation[i + '.params'] = { + "regexp.title": "{string} regular expression to match incorrect " + + streetName + " name", + "regexp": streetRegExp, + }; + } + // Generate mirror checks + mirrorChecks(defTranslation); + + // init internal translations + var listOfIntPacks = ''; + for(var translationsKey in _translations) + { + var translation = _translations[translationsKey]; + mirrorChecks(translation); + _I18n.addTranslation(translation); + + // update listOfIntPacks + var country = translation[".country"]; + if(!country) continue; + if(classCodeIs(country, CC_ARRAY)) + country = country[0]; + country = country.split(' ').join(' ') + + if(listOfIntPacks) + listOfIntPacks += ', '; + if(".lng" in translation) + listOfIntPacks += '' + country + '*'; + else + listOfIntPacks += country; + + if(".author" in translation) + { + listOfIntPacks += ' by ' + translation[".author"]; + } + if(".lng" in translation) + listOfIntPacks += ''; + if(".updated" in translation) + listOfIntPacks += ' (' + translation[".updated"] + ')'; + } + listOfIntPacks += '.'; + listOfIntPacks += '
* localization pack with translations'; + + // add external translations + var listOfPacks = ''; + for(var gObject in window) + { + if(!window.hasOwnProperty(gObject)) continue; + if(-1 !== gObject.indexOf(WV_NAME_)) + { + var translation = window[gObject]; + log("found localization pack: " + gObject.replace(WV_NAME_ + '_', '')); + mirrorChecks(translation); + _I18n.addTranslation(translation); + + // update listOfPacks + if(".country" in translation) + { + var country = translation[".country"]; + if(classCodeIs(country, CC_ARRAY)) + country = country[0]; + listOfPacks += '' + country; + + if(".author" in translation) + listOfPacks += ' by ' + translation[".author"]; + listOfPacks += ''; + + if(!(".lng" in translation)) + listOfPacks += '
(does not include translations)'; + + if(".updated" in translation) + { + listOfPacks += '
Updated: ' + translation[".updated"]; + if(".link" in translation + && translation[".link"]) + listOfPacks += '
check for updates'; + } + listOfPacks += '
'; + } + } + } + listOfPacks = (listOfPacks ? listOfPacks : "No external localization packs found"); + listOfPacks += '
See ' + + 'how to create a localization pack'; + + // Generate $checks + for(var i = 1; i < MAX_CHECKS; i++) + { + var check = { + ENABLED: {}, + PROBLEMLINK: {}, + PROBLEMLINKTEXT: {}, + SOLUTIONLINK: {}, + SOLUTIONLINKTEXT: {}, + }; + + // set title + var label = i + '.title'; + if(!_I18n.isLabelExist(label)) + continue; + check.TITLE = trS(label); + + label = i + '.color'; + if(_I18n.isLabelExist(label)) + { + var col = trS(label).toUpperCase(); + check.COLOR = col; + // Generate $WMECHcolors + if(CK_WMECHFIRST <= i + && CK_WMECHLAST >= i) + _RT.$WMECHcolors[col] = true; + } + label = i + '.problem'; + if(_I18n.isLabelExist(label)) + check.PROBLEM = trS(label); + label = i + '.solution'; + if(_I18n.isLabelExist(label)) + check.SOLUTION = trS(label); + label = i + '.reportOnly'; + if(_I18n.isLabelExist(label)) + check.REPORTONLY = trS(label); + label = i + '.severity'; + var s = 'N'; + if(_I18n.isLabelExist(label)) + s = trS(label); + if(s) + { + switch(s.charAt(0)) + { + case "w": + case "W": + check.SEVERITY = RS_WARNING; + break; + case "e": + case "E": + check.SEVERITY = RS_ERROR; + break; + case "1": + check.SEVERITY = RS_CUSTOM1; + break; + case "2": + check.SEVERITY = RS_CUSTOM2; + break; + default: + check.SEVERITY = RS_NOTE; + break; + } + } + else + check.SEVERITY = RS_NOTE; + + // set country-dependant params + label = i + '.enabled'; + var labelP = i + '.params'; + var labelPL = i + '.problemLink'; + var labelSL = i + '.solutionLink'; + + var defEnabled = false; + var arrCodes = []; + for(var ccode in _I18n.$translations) + { + var translation = _I18n.$translations[ccode]; + if(label in translation) + { + var e = translation[label]; + check.ENABLED[ccode] = e; + // create FORCOUNTRY + if(_I18n.$defLng === ccode) + { + if(e) defEnabled = true; + } + else + { + if(e) + arrCodes.push(ccode); + else + arrCodes.push('!' + ccode); + } + } + + if(labelPL in translation) + { + var l = translation[labelPL] + .replace('W:', PFX_WIKI) + .replace('F:', PFX_FORUM) + ; + check.PROBLEMLINK[ccode] = encodeURI(l); + if(-1 !== l.indexOf(PFX_WIKI)) + check.PROBLEMLINKTEXT[ccode] = trS('report.link.wiki'); + else + if(-1 !== l.indexOf(PFX_FORUM)) + check.PROBLEMLINKTEXT[ccode] = trS('report.link.forum'); + else + check.PROBLEMLINKTEXT[ccode] = trS('report.link.other'); + } + if(labelSL in translation) + { + var l = translation[labelSL] + .replace('W:', PFX_WIKI) + .replace('F:', PFX_FORUM) + ; + check.SOLUTIONLINK[ccode] = encodeURI(l); + if(-1 !== l.indexOf(PFX_WIKI)) + check.SOLUTIONLINKTEXT[ccode] = trS('report.link.wiki'); + else + if(-1 !== l.indexOf(PFX_FORUM)) + check.SOLUTIONLINKTEXT[ccode] = trS('report.link.forum'); + else + check.SOLUTIONLINKTEXT[ccode] = trS('report.link.other'); + } + if(labelP in translation) + { + var params = translation[labelP]; + if(!check.OPTIONS) + check.OPTIONS = {}; + if(!(ccode in check.OPTIONS)) + check.OPTIONS[ccode] = params; + + if(params["template"]) + check.OPTIONS[ccode][CO_STRING] = params["template"]; + if(params["regexp"]) + _WV.buildRegExp(i, check.OPTIONS[ccode], params["regexp"]); + if(params["n"]) + check.OPTIONS[ccode][CO_NUMBER] = +params["n"]; + } // .params + } // for codeKeys + // create FORCOUNTRY + if(defEnabled) + { + if(arrCodes.length) + check.FORCOUNTRY = arrCodes.join(',') + ',*'; + } + else + { + if(arrCodes.length) + check.FORCOUNTRY = arrCodes.join(','); + else + check.FORCOUNTRY = '!*'; + } + + _RT.$checks[i] = check; + } +// window.console.log(_RT.$checks); + + /////////////////////////////////////////////////////////////////////// + // User interface + var dir = _I18n.getDir(); + var dirLeft = trLeft(dir); + var dirRight = trRight(dir); + // Waze-styled tabs + var cssRules, cssRules2, cssRulesA = ">a{text-decoration:underline;cursor:pointer;pointer-events:auto}"; + _THUI.addElemetClassStyle("div", CL_TABS, "{border-bottom:2px solid #ddd;height:29px}"); + _THUI.addElemetClassStyle("div", CL_TABS, ">input{display:none}"); + _THUI.addElemetClassStyle("div", CL_TABS, ">label{white-space:nowrap;overflow:hidden;max-width:100px;text-overflow:ellipsis;cursor:pointer;display:inline-block;margin:0px;margin-" + dirRight + ":3px;padding:4px 12px;border-radius:4px 4px 0 0;background-color:#dadbdc}"); + _THUI.addElemetClassStyle("div", CL_TABS, ">input:checked+label{font-weight:normal;margin:-2px;min-height:31px;margin-" + dirRight + ":2px;cursor:default;border:2px solid #ddd;border-bottom-color:#fff;background-color:#fff}"); + // !important for WME beta + _THUI.addElemetClassStyle("div", CL_TABS, ">input:disabled+label{font-weight:bold !important;padding-" + dirLeft + ":0px;color:#333;cursor:default;background-color:transparent}"); + _THUI.addElemetClassStyle("div", CL_TABS, ">input:enabled+label:hover{background-color:#fff}"); + _THUI.addElemetClassStyle("div", CL_TABS, ">input:checked+label:hover{background-color:#fff}"); + _THUI.addElemetClassStyle("div", CL_TABS, ">input:enabled+label>span>span.c" + CL_COLLAPSE + "{display:none}"); + _THUI.addElemetClassStyle("div", CL_TABS, ">input:checked+label>span>span.c" + CL_COLLAPSE + "{display:inline}"); + // Collapsing tab + // _THUI.addElemetClassStyle("ul", "nav", ">li.active>a>span+span.c" + CL_COLLAPSE + "{display:inline}"); + // _THUI.addElemetClassStyle("ul", "nav", ">li>a>span+span.c" + CL_COLLAPSE + "{display:none}"); + // fixed height panel + _THUI.addElemetClassStyle("div", CL_PANEL, "{background-color:#fff;padding:4px;margin:0;margin-bottom:4px;border-bottom:2px solid #ddd;white-space:nowrap;overflow-x:hidden;overflow-y:auto;text-overflow:ellipsis;width:100%;height:" + SZ_PANEL_HEIGHT + "px}"); + _THUI.addElemetClassStyle("div", CL_PANEL, ">span" + cssRulesA); + _THUI.addElemetClassStyle("div", CL_PANEL, ">label>span" + cssRulesA); + _THUI.addElemetClassStyle("div", CL_PANEL, ">span>p" + cssRulesA); + // custom color markers + cssRules = ">span{border-radius:5px;background-color:"; + _THUI.addElemetClassStyle("label", "c1", cssRules + + GL_CUSTOM1COLOR + ";color:" + GL_CUSTOM1BGCOLOR + "}"); + _THUI.addElemetClassStyle("label", "c2", cssRules + + GL_CUSTOM2COLOR + ";color:" + GL_CUSTOM2BGCOLOR + "}"); + cssRules = ">span>a{color:white}"; + _THUI.addElemetClassStyle("label", "c1", cssRules); + _THUI.addElemetClassStyle("label", "c2", cssRules); + // buttons +// "{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}"); + _THUI.addElemetClassStyle("div", CL_BUTTONS, "{overflow:hidden;margin-bottom:1em}"); + _THUI.addElemetClassStyle("div", CL_BUTTONS, ">button{font-weight:normal;padding:4px 12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}"); + _THUI.addElemetClassStyle("div", CL_BUTTONS, ">button>i{pointer-events:none}"); + _THUI.addElemetClassStyle("div", CL_BUTTONS, ">button:disabled{background-color:#eee;border-bottom:0px;cursor:default;pointer-events:auto}"); + // checkboxes + _THUI.addElemetClassStyle("div", CL_PANEL, ">label.checkbox{display:block;height:24px;font-weight:normal;margin:0}"); + _THUI.addElemetClassStyle("div", CL_PANEL, ">label.checkbox>span{display:inline-block;height:20px;width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}"); + // date + _THUI.addElemetClassStyle("div", CL_PANEL, ">label.date{display:block;height:32px;font-weight:normal;margin:0;padding-" + dirRight + ":155px}"); + _THUI.addElemetClassStyle("div", CL_PANEL, ">label.date>span{display:inline-block;line-height:28px;vertical-align:middle;width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}"); + _THUI.addElemetClassStyle("div", CL_PANEL, ">label.date>input[type=date]{box-sizing:border-box;height:28px;padding:2px 10px;padding-" + dirRight + ":2px;float:" + dirRight + ";margin-" + dirRight + ":-155px;width:150px}"); + // text + _THUI.addElemetClassStyle("div", CL_PANEL, ">label.text{display:block;height:30px;font-weight:normal;margin:0;padding-" + dirRight + ":155px}"); + _THUI.addElemetClassStyle("div", CL_PANEL, ">label.text>span{display:inline-block;line-height:28px;vertical-align:middle;width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}"); + _THUI.addElemetClassStyle("div", CL_PANEL, ">label.text>input[type=text]{box-sizing:border-box;height:28px;padding:2px 10px;float:" + dirRight + ";margin-" + dirRight + ":-155px;width:150px}"); + // number inputs +// _THUI.addElemetClassStyle("div", CL_PANEL, ">label.number{font-weight:normal;display:block;line-height:28px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}"); +// _THUI.addElemetClassStyle("div", CL_PANEL, ">label.number>span{float:" + dirLeft + "}"); +// _THUI.addElemetClassStyle("div", CL_PANEL, ">label.number>input[type=number]{box-sizing:border-box;display:inline-block;width:64px;height:26px;line-height:20px;border-width:1px;float:" + dirRight + ";padding:2px 6px;margin:0}"); + // password inputs +// _THUI.addElemetClassStyle("div", CL_PANEL, ">input[type=password]{box-sizing:border-box;width:270px;height:28px}"); +// cssRules = '' + // show translate and report banner + cssRules = "{position:relative;height:2em;width:100%;margin-bottom:"; + cssRules2 = ">span{position:absolute;" + dirLeft + ":0;bottom:0;display:inline-block;padding:4px 12px;margin:0px;border-radius:8px;border-bottom-" + dirLeft + "-radius:0;box-shadow:3px 3px 3px #aaa;border:1px solid "; + _THUI.addElemetClassStyle("div", CL_TRANSLATETIP, cssRules + "12px}"); + _THUI.addElemetClassStyle("div", CL_TRANSLATETIP, cssRules2 + "#aea;background-color:#cfc;" + dirLeft + ":auto;" + dirRight + ":0;border-radius:8px;border-bottom-" + dirRight + "-radius:0}"); + _THUI.addElemetClassStyle("div", CL_TRANSLATETIP, ">span" + cssRulesA); + // message box + _THUI.addElemetClassStyle("div", CL_MSG, cssRules + "1em}"); + _THUI.addElemetClassStyle("div", CL_MSG, cssRules2 + "#ded;background-color:#efe}"); + _THUI.addElemetClassStyle("div", CL_MSGY, cssRules + "1em}"); + _THUI.addElemetClassStyle("div", CL_MSGY, cssRules2 + "#ee9;background-color:#ffa}"); + // segment properties + _THUI.addElemetIdStyle("div", ID_PROPERTY, "{padding-bottom:5px}"); + _THUI.addElemetIdStyle("div", ID_PROPERTY, ">b" + cssRulesA); + cssRules = '{color:' + GL_NOTECOLOR + '}'; + _THUI.addElemetClassStyle("div", CL_NOTE, cssRules); + _THUI.addElemetClassStyle("a", CL_NOTE, cssRules); + cssRules = '{color:' + GL_WARNINGCOLOR + '}'; + _THUI.addElemetClassStyle("div", CL_WARNING, cssRules); + _THUI.addElemetClassStyle("a", CL_WARNING, cssRules); + cssRules = '{color:' + GL_ERRORCOLOR + '}'; + _THUI.addElemetClassStyle("div", CL_ERROR, cssRules); + _THUI.addElemetClassStyle("a", CL_ERROR, cssRules); + cssRules = '{color:' + GL_CUSTOM1COLOR + '}'; + _THUI.addElemetClassStyle("div", CL_CUSTOM1, cssRules); + _THUI.addElemetClassStyle("a", CL_CUSTOM1, cssRules); + cssRules = '{color:' + GL_CUSTOM2COLOR + '}'; + _THUI.addElemetClassStyle("div", CL_CUSTOM2, cssRules); + _THUI.addElemetClassStyle("a", CL_CUSTOM2, cssRules); + _THUI.addElemetClassStyle("div", CL_RIGHTTIP, "{white-space:nowrap;position:relative;cursor:help}"); + _THUI.addElemetClassStyle("div", CL_RIGHTTIP, ">span{display:inline-block;overflow:hidden;text-overflow:ellipsis;width:279px}"); + _THUI.addElemetClassStyle("div", CL_RIGHTTIP, ">span" + cssRulesA); + cssRules = ';z-index:1000000;position:absolute;visibility:hidden;opacity:0;transition:0.1s ease;' + dirLeft + ':30px;top:-1.7em;cursor:default}'; + _THUI.addElemetClassStyle('div', CL_RIGHTTIP, ':before{content:"";position:absolute;border:1em solid transparent;border-' + dirRight + '-color:#ddd;margin-' + dirLeft + ':-2em;margin-top:1.5em' + cssRules); + _THUI.addElemetClassStyle('div', CL_RIGHTTIPPOPUP, '{white-space:normal;background-color:#fafafa;padding:1em;width:230px;box-shadow:3px 3px 3px #aaa;border-radius:1em;border:1px solid #ddd' + cssRules); + _THUI.addElemetClassStyle('div', CL_RIGHTTIPDESCR, '{margin-' + dirLeft + ':2em}'); + _THUI.addElemetClassStyle('div', CL_RIGHTTIPDESCR, cssRulesA); + _THUI.addElemetClassStyle('div', CL_RIGHTTIPDESCR, '>p{color:black;margin-top:0.5em;margin-bottom:0.5em !important}'); + _THUI.addElemetClassStyle('div', CL_RIGHTTIPDESCR, '>p' + cssRulesA); + cssRules = '{visibility:visible;opacity:1}'; + _THUI.addElemetClassStyle('div', CL_RIGHTTIP, ':hover:before' + cssRules); + _THUI.addElemetClassStyle('div', CL_RIGHTTIP, ':hover>div' + cssRules); + + _UI = { + // set to defaults to suppress warnings + _DISABLED: undefined, + _NODISPLAY: undefined, + MAXLENGTH: undefined, + REVERSE: undefined, + WARNING: undefined, + TYPE: undefined, + FORUSER: undefined, + FORCITY: undefined, + FORCOUNTRY: undefined, + FORLEVEL: undefined, + ACCESSKEY: undefined, + STYLEI: "", +// STYLEO: "", + DISCLOSE: 0, + _NAME: "", + READONLY: 0, + _STYLE: "", + ONWARNING: null, + ONCHANGE: null, + _ONCHANGE: undefined, + ONCLICKO: undefined, + MIN: undefined, + MAX: undefined, + STEP: undefined, + CLASS: CL_UI, + + // global params + _TYPE: _THUI.DIV, + _ONWARNING: onWarning, + pTips: { + }, + pTranslateBanner: { + CLASS: CL_TRANSLATETIP, +// NODISPLAY: 1, + TEXT: "Please help to " + + '' + + 'translate Validator!', + TITLE: trS("about.tip"), + }, + pNoAccess: { + CLASS: CL_PANEL, + NODISPLAY: 1, + STYLE: 'text-align:center', + TEXT: trS("noaccess.text"), + TITLE: trS("noaccess.tip"), + }, + pMain: { + pTabs: { + CLASS: CL_TABS, + _DISCLOSE: 1, + _TYPE: _THUI.RADIO, + _ONCLICK: onUpdateUI, + tMain: { + // text and title update automatically + TEXT: '', + TITLE: '', + DISABLED: 1, + STYLEO: "cursor:pointer;max-width:97px", + ONCLICKO: onUpdateUI + }, + tFilter: { + TEXT: '' + " " + trS("tab.filter.text") + "", + TITLE: trS("tab.filter.tip"), + CHECKED: 1 + }, + tSearch: { + TEXT: '' + " " + trS("tab.search.text") + "", + TITLE: trS("tab.search.tip"), + }, + tHelp: { + TEXT: '' + " " + trS("tab.help.text") + "", + TITLE: trS("tab.help.tip"), + STYLEO: "float:" + dirRight + }, + }, // pMain.pTabs + pFilter: { + CLASS: CL_PANEL, + _CLASS: "checkbox", + _TYPE: _THUI.CHECKBOX, + _REVERSE: 1, + _ONCHANGE: onUpdateUI, + oExcludeNonEditables: { + TEXT: trS("filter.noneditables.text"), + TITLE: trS("filter.noneditables.tip"), + AUTOSAVE: AS_NONEDITABLES + }, + oExcludeDuplicates: { + TEXT: trS("filter.duplicates.text"), + TITLE: trS("filter.duplicates.tip"), + AUTOSAVE: AS_DUPLICATES + }, + oExcludeStreets: { + TEXT: trS("filter.streets.text"), + TITLE: trS("filter.streets.tip"), + AUTOSAVE: AS_STREETS + }, + oExcludeOther: { + TEXT: trS("filter.other.text"), + TITLE: trS("filter.other.tip"), + AUTOSAVE: AS_OTHERS + }, + oExcludeNotes: { + TEXT: trS("filter.notes.text"), + TITLE: trS("filter.notes.tip"), + AUTOSAVE: AS_NOTES + }, + }, // pMain.pFilter + pSearch: { + CLASS: CL_PANEL, + NODISPLAY: 1, +// _CLASS: "checkbox", +// _TYPE: _THUI.CHECKBOX, + _REVERSE: 1, + _ONCHANGE: onUpdateUI, + oIncludeYourEdits: { + NODISPLAY: 1, + TYPE: _THUI.CHECKBOX, + TEXT: trS("search.youredits.text"), + TITLE: trS("search.youredits.tip"), + CLASS: "checkbox", + AUTOSAVE: AS_YOUREDITS + }, + oIncludeUpdatedBy: { +// NODISPLAY: 1, + TYPE: _THUI.TEXT, + TEXT: trS("search.updatedby.text"), + TITLE: trS("search.updatedby.tip"), + PLACEHOLDER: trS("search.updatedby.example"), + CLASS: "form-label text", + CLASSI: "form-control", + AUTOSAVE: AS_UPDATEDBY + }, + oIncludeUpdatedSince: { + TYPE: _THUI.DATE, + TEXT: trS("search.updatedsince.text"), + TITLE: trS("search.updatedsince.tip"), + PLACEHOLDER: trS("search.updatedsince.example"), + CLASS: "form-label date", + CLASSI: "form-control", + AUTOSAVE: AS_UPDATEDSINCE + }, + oIncludeCityName: { + TYPE: _THUI.TEXT, + TEXT: trS("search.city.text"), + TITLE: trS("search.city.tip"), + PLACEHOLDER: trS("search.city.example"), + CLASS: "form-label text", + CLASSI: "form-control", + AUTOSAVE: AS_CITYNAME + }, + oIncludeChecks: { + TYPE: _THUI.TEXT, + TEXT: trS("search.checks.text"), + TITLE: trS("search.checks.tip"), + PLACEHOLDER: trS("search.checks.example"), + CLASS: "form-label text", + CLASSI: "form-control", + AUTOSAVE: AS_CHECKS + }, + }, // pMain.pSearch + pHelp: { + CLASS: CL_PANEL, + NODISPLAY: 1, + TEXT: trS("help.text"), + TITLE: trS("help.tip"), + }, // pMain.pHelp + pButtons: { + CLASS: CL_BUTTONS, + _CLASS: "btn btn-default", + _TYPE: _THUI.BUTTON, + _ONCLICK: onUpdateUI, + bScan: { + TEXT: "\uf04b", + // title updates automatically + TITLE: "", + STYLE: "float:" + dirLeft + ";width:38px;font-family:FontAwesome" + }, + bPause: { + NODISPLAY: 1, + TEXT: "\uf04c", + TITLE: trS("button.pause.tip"), + STYLE: "float:" + dirLeft + ";width:38px;font-family:FontAwesome" + }, + bContinue: { + TEXT: "\uf04b", + TITLE: trS("button.continue.tip"), + NODISPLAY: 1, + STYLE: "float:" + dirLeft + ";width:38px;font-family:FontAwesome" + }, + bStop: { + TEXT: "\uf04d", + TITLE: trS("button.stop.tip"), + STYLE: "float:" + dirLeft + ";width:38px;font-family:FontAwesome;margin-" + dirRight + ":10px" + }, + bClear: { +// TEXT: "\uf016", + TEXT: "\u2718", + // title updates automatically + TITLE: "", + NODISPLAY: 1, + DISABLED: 1, + STYLE: "float:" + dirLeft + ";width:38px;margin-" + dirRight + ":10px" + }, + bReport: { + TEXT: trS("button.report.text"), + TITLE: trS("button.report.tip"), + STYLE: "float:" + dirLeft + ";max-width:110px", + ONCLICK: onShowReport + }, + bReportBB: { +// TEXT: "\uf003", + TEXT: "\uf0e0", + TITLE: trS("button.BBreport.tip"), + ONCLICK: onShareReport, + STYLE: "float:" + dirLeft + ";width:38px;font-family:FontAwesome" + }, + bSettings: { + TEXT: "\uf013", + TITLE: trS("button.settings.tip"), + STYLE: "float:" + dirRight + ";width:38px;font-family:FontAwesome" + }, + } // pMain.pButtons + }, // pMain + pSettings: { + NODISPLAY: 1, + pTabs: { + CLASS: CL_TABS, + _DISCLOSE: 1, + _TYPE: _THUI.RADIO, + _ONCLICK: onUpdateUI, + tMain: { + TEXT: trS("tab.settings.text") + ':', + TITLE: WV_NAME + " Version " + WV_VERSION, + STYLEO: "max-width:85px", + DISABLED: 1 + }, + tCustom: { + TEXT: '' + " " + trS("tab.custom.text") + "", + STYLEO: "max-width:110px", + TITLE: trS("tab.custom.tip"), + CHECKED: 1 + }, + tScanner: { + TEXT: '' + " " + trS("tab.scanner.text") + "", + TITLE: trS("tab.scanner.tip"), + STYLEO: "max-width:110px", + }, + tAbout: { + TEXT: '' + " " + trS("tab.about.text") + "", + TITLE: trS("tab.about.tip"), + STYLEO: "float:" + dirRight + ";max-width:110px" + } + }, // pSettings.pTabs + pCustom: { + CLASS: CL_PANEL, + _CLASS: "form-label text", + _REVERSE: 1, + _ONCHANGE: onUpdateUI, + oTemplate1: { + TYPE: _THUI.TEXT, + TEXT: ' ' + trS("custom.template.text"), + TITLE: trS("custom.template.tip"), + PLACEHOLDER: trS("custom.template.example"), + CLASS: "form-label text c1", + CLASSI: "form-control", + AUTOSAVE: AS_CUSTOM1TEMPLATE + }, + oRegExp1: { + TYPE: _THUI.TEXT, + TEXT: ' ' + trS("custom.regexp.text"), + TITLE: trS("custom.regexp.tip"), + PLACEHOLDER: trS("custom.regexp.example"), + CLASSI: "form-control", + AUTOSAVE: AS_CUSTOM1REGEXP + }, + oTemplate2: { + TYPE: _THUI.TEXT, + TEXT: ' ' + trS("custom.template.text"), + TITLE: trS("custom.template.tip"), + PLACEHOLDER: trS("custom.template.example"), + CLASS: "form-label text c2", + CLASSI: "form-control", + AUTOSAVE: AS_CUSTOM2TEMPLATE + }, + oRegExp2: { + TYPE: _THUI.TEXT, + TEXT: ' ' + trS("custom.regexp.text"), + TITLE: trS("custom.regexp.tip"), + PLACEHOLDER: trS("custom.regexp.example"), + CLASSI: "form-control", + AUTOSAVE: AS_CUSTOM2REGEXP + }, + }, // pSettings.pCustom + pScanner: { + NODISPLAY: 1, + CLASS: CL_PANEL, + _CLASS: "checkbox", + _TYPE: _THUI.CHECKBOX, + _REVERSE: 1, + _ONCHANGE: onUpdateUI, + oSlowChecks: { + TEXT: trS("scanner.slow.text"), + TITLE: trS("scanner.slow.tip"), + AUTOSAVE: AS_SLOWCHECKS + }, + oReportExt: { + TEXT: trS("scanner.ext.text"), + TITLE: trS("scanner.ext.tip"), + AUTOSAVE: AS_REPORTEXT + }, + oHLReported: { + TEXT: trS("scanner.highlight.text"), + TITLE: trS("scanner.highlight.tip"), + AUTOSAVE: AS_HLISSUES + }, + oSounds: { + TEXT: trS("scanner.sounds.text"), + TITLE: trS("scanner.sounds.tip"), + NATITLE: trS("scanner.sounds.NA"), + AUTOSAVE: AS_SOUNDS + }, + }, // pSettings.pScanner + pAbout: { + CLASS: CL_PANEL, + NODISPLAY: 1, + TEXT: '

' + WV_NAME + '' + + '
Version ' + WV_VERSION + ' check for updates' + + '
Expiration date: ' + WV_RELEASE_VALID + + '
© 2013-2016 berestovskyy

' + + '

Built-in localization packs for:
' + + listOfIntPacks + + '

External localization packs for:
' + + listOfPacks + + '

' + + '

Special thanks to:
OyyoDams, Timbones, paulkok_my, petervdveen, MdSyah, sketch, AlanOfTheBerg, arbaot, Zniwek, orbitc, robindlc, fernandoanguita, BellHouse, vidalnit, Manzareck, gad_m, Zirland and YOU!

', + TITLE: trS("about.tip"), + STYLE: 'direction:ltr;text-align:center;white-space:normal' + }, // pSettings.pAbout + pButtons: { + CLASS: CL_BUTTONS, + _CLASS: "btn btn-default", + _TYPE: _THUI.BUTTON, + _ONCLICK: onUpdateUI, + bReset: { + TEXT: ' ' + trS("button.reset.text"), + TITLE: trS("button.reset.tip"), + STYLE: "float:" + dirLeft + ";max-width:165px", + }, + bList: { + NODISPLAY: 1, + TEXT: ' ' + trS("button.list.text"), + TITLE: trS("button.list.tip"), + STYLE: "float:" + dirLeft + ";max-width:165px", + ONCLICK: onShowChecks + }, + bWizard: { + NODISPLAY: 1, + TEXT: '', + TITLE: trS("button.wizard.tip"), + STYLE: "float:" + dirLeft + ";margin-" + dirLeft + ":6px;width:38px", + ONCLICK: onCreatePack + }, + bBack: { + TEXT: ' ' + trS("button.back.text"), + TITLE: trS("button.back.tip"), + STYLE: "float:" + dirRight + ";max-width:70px" + } + } // pSettings.pButtons + } // pSettings + }; // _UI + + // init report + clearReport(); + + // check if user is a country manager + if(_RT.$topUser.$isCM || _RT.$topUser.$isSuperUser) + { + _UI.pMain.pSearch.oIncludeYourEdits.NODISPLAY = 1; + _UI.pMain.pSearch.oIncludeUpdatedBy.NODISPLAY = 0; + } + else + { + _UI.pMain.pSearch.oIncludeYourEdits.NODISPLAY = 0; + _UI.pMain.pSearch.oIncludeUpdatedBy.NODISPLAY = 1; + } + + // show translate banner + if(-1 !== _RT.$untranslatedLngs.indexOf(_RT.$lng.split('-')[0])) + _UI.pTranslateBanner.NODISPLAY = 0; + else + _UI.pTranslateBanner.NODISPLAY = 1; + + + // check for AudioContext + if(!classCodeDefined(UW.AudioContext) + && !classCodeDefined(UW.webkitAudioContext)) + { + _UI.pSettings.pScanner.oSounds.CHECKED = false; + _UI.pSettings.pScanner.oSounds.NA = true; + } + + // reset defaults + resetDefaults(); + + // load saved values from local storage + var storageObj = null; + var s = null; + try + { + s = window.localStorage.getItem(AS_NAME); + storageObj = s ? JSON.parse(Tea.decrypt(s, AS_PASSWORD)) : null; + } + catch (e) {} + + if(!storageObj || WV_LICENSE_VERSION !== storageObj[AS_LICENSE]) + { + if(!confirm(WV_LICENSE)) + { + // destroy UI + _UI = {}; + // uninstall login/logout handler + WLM.events.un({ + "afterloginchanged": onLogin, + "login": onLogin + }); + return; + } + } + + var showWhatsNew = false; + if(s && !storageObj) + { + warning("\nDue to the major changes in Validator, all filter options\nand settings have been RESET to their DEFAULTS."); + showWhatsNew = true; + } + if(storageObj && WV_VERSION !== storageObj[AS_VERSION]) + showWhatsNew = true; + + if(showWhatsNew) + info(WV_WHATSNEW); + + _THUI.loadValues(_UI, storageObj); + + // create a styleMap with a custom default symbolizer + var styleMap = new OpenLayers.StyleMap({ + strokeWidth: HL_WIDTH, + }); + + // create a lookup table with different symbolizers for 0, 1 and 2 + var lookup = {}; + lookup[RS_NOTE] = { + strokeColor: GL_NOTECOLOR, + graphicZIndex: 10, + }; + lookup[RS_WARNING] = { + strokeColor: GL_WARNINGCOLOR, + graphicZIndex: 20, + }; + lookup[RS_ERROR] = { + strokeColor: GL_ERRORCOLOR, + graphicZIndex: 30, + }; + lookup[RS_CUSTOM2] = { + strokeColor: GL_CUSTOM2COLOR, + graphicZIndex: 40, + }; + lookup[RS_CUSTOM1] = { + strokeColor: GL_CUSTOM1COLOR, + graphicZIndex: 50, + }; + + // add rules from the above lookup table, with the keyes mapped to + // the "type" property of the features, for the "default" intent + styleMap.addUniqueValueRules("default", 0, lookup); + + _RT.$HLlayer = new OpenLayers.Layer.Vector(GL_LAYERNAME, { + displayInLayerSwitcher: true, + renderers: [R("Waze/Renderer/ExtendedSVG")], + // enable the indexer by setting zIndexing to true + rendererOptions: {zIndexing: true}, + uniqueName: GL_LAYERUNAME, + shortcutKey: GL_LAYERSHORTCUT, + accelerator: GL_LAYERACCEL, + units: "m", + + styleMap: styleMap, + projection: new OpenLayers.Projection("EPSG:4326"), + visibility: _UI.pSettings.pScanner.oHLReported.CHECKED, + }); + I18n.translations.en.layers.name[GL_LAYERUNAME] = GL_LAYERNAME; + _RT.$HLlayer.setOpacity(HL_OPACITY); + WM.addLayer(_RT.$HLlayer); + _RT.$HLlayer.setVisibility(_UI.pSettings.pScanner.oHLReported.CHECKED); + WM.raiseLayer(_RT.$HLlayer, 99); + + // Create Validator tab + $('#user-tabs ul').append('
  • ' + + '' + + '' + // + ' ' + WV_SHORTNAME + ':' + + ' ' + WV_SHORTNAME + + '
  • ' + ); + $('#user-tabs+div.tab-content').append( + '
    ' + ); + // append user interface after the details or ad the bottom + _THUI.appendUI(document.getElementById("sidepanel-" + ID_PREFIX), + _UI, "i" + ID_PREFIX); + + async(F_UPDATEUI); + // for the highlights + async(ForceHLAllSegments, null, 700); + + // register event handlers + WMo.events.on({ + "mergeend": onMergeEnd, + }); + WM.events.on({ + "moveend": onMoveEnd, + "zoomend": HLAllSegments, + "changelayer": onChangeLayer, + }); + WSM.events.on({ + "selectionchanged": ForceHLAllSegments + }); + WC.events.on({ + "loadstart": onLoadStart, + }); + + // monitor segments and nodes changes + // TODO: fix me to WMo.segments.on + // WMo.segments.events.on({ + // "objectsadded": onSegmentsAdded, + // "objectschanged": onSegmentsChanged, + // "objectsremoved": onSegmentsRemoved, + // }); + // WMo.nodes.events.on({ + // "objectschanged": onNodesChanged, + // "objectsremoved": onNodesRemoved, + // }); +} diff --git a/src/release.js b/src/release.js new file mode 100644 index 0000000..8a23d0f --- /dev/null +++ b/src/release.js @@ -0,0 +1,151 @@ +/* + * release.js -- WME Validator release information + * Copyright (C) 2013-2018 Andriy Berestovskyy + * + * This file is part of WME Validator: + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * Note: This code is heavily based on the GNU MP Library. + * Actually it's the same code with only minor changes in the + * way the data is stored; this is to support the abstraction + * of an optional secure memory allocation which may be used + * to avoid revealing of sensitive data due to paging etc. + * The GNU MP Library itself is published under the LGPL; + * however I decided to publish this code under the plain GPL. + */ + +/************************************************************************* + * RELEASE INFORMATION + *************************************************************************/ + +/** + * WV RELEASE INFORMATION + */ +/** CHECKSUM @const */ +var CF_SUMBLUE = 329630; +/** @const */ +var WV_SHORTNAME = "Validator"; +/** @const */ +var WV_NAME = "WME " + WV_SHORTNAME; +/** @const */ +var WV_NAME_ = WV_NAME.split(" ").join("_"); +/** @const */ +var WV_NAME_NO_SPACE = WV_NAME.split(" ").join(""); +/** @const */ +var WV_ABBREVIATION = "WV"; +/** @const */ +var WV_RELEASE_DATE = "2016-11-03"; +/** @const */ +var WV_RELEASE_VALID = "2017-07-01"; +/* Version: 1.1.20 */ +/** @const */ +var WV_MAJOR = "1.1"; +/** @const */ +var WV_MINOR = "20"; +/** Auto-save password @const */ +var AS_PASSWORD = "3"; +/** @const */ +var WV_VERSION = WV_MAJOR + "." + WV_MINOR; +/** @const */ +var WV_WHATSNEW = "" + + "- Fixed #23 Unconfirmed road" + + "\n" + + "\n04.06.2016 v1.1.19:" + + "\n- Fixed WME Beta" + + "\n- Fixed icons in segment properties" + + "\n- The work is still in progress..." + + "\n" + + "\n02.06.2016 v1.1.18:" + + "\n- Fixed Firefox browser" + + "\n- Added Validator tab" + + "\n" + + "\n01.06.2016 v1.1.17:" + + "\n- Fixed (some) icons" + + "\n- Fixed (some) event handlers" + + "\n" + + "\n29.01.2016 v1.1.16:" + + "\n- Fixed Firefox browser (thanks to Glodenox)" + + "\n- Updated CZ localizations" + + "\n" + + "\n13.12.2015 v1.1.15:" + + "\n- Updated US and CZ localizations" + ; +/** @const */ +var WV_LICENSE_VERSION = "1"; +/** @const */ +var WV_LICENSE = "" + + "\nLICENSE:" + + "\nThis script has some features locked for certain" + + "\ncountries/editor levels, so there are 2 restrictions:" + + "\n" + + "\n1. You may not modify or reverse engineer the script." + + "\n2. You may not use the script for commercial purposes." + + "\n" + + "\nThe script is distributed 'as is'. No warranty of any" + + "\nkind is expressed or implied. You use at your own risk." + + "\n" + + "\nIf you do not agree with the terms of this license," + + "\nyou must remove the script files from your storage" + + "\ndevices and cease to use " + WV_NAME + '.' + + "\n" + + "\nNote: " + WV_NAME + " uses local storage to remember" + + "\nyour choices and preferences." + ; + +/** + * WV global access lists + */ +/** @const */ +var GA_FORLEVEL = 1; +/** @const */ +var GA_FORUSER = "!Dekis,*"; +/** @const */ +var GA_LEVEL1 = 1; +/** + * Users allowed to lookup other users + * @const + */ +var GA_SUPERUSER = "berestovskyy"; + +/** @const */ +var GA_FORCOUNTRY = ""; +/** @const */ +//var GA_FORCITY = ""; +var GA_FORCITY = "!Kraków,*"; +/** @const */ +var LIMIT_TOTAL = 2e4; +//var LIMIT_TOTAL = 2e6; +/** + * Maxs + */ +/** @const */ +var MAX_CHECKS = 310; + +/************************************************************************* + * URLs + *************************************************************************/ +/** @const */ +var PFX_WIKI = 'https://www.waze.com/wiki/'; +/** @const */ +var PFX_FORUM = 'https://www.waze.com/forum/viewtopic.php?'; +/** @const */ +var FORUM_HOME = 't=76488'; +/** @const */ +var FORUM_FAQ = 't=76488&p=666476#p666476'; +/** @const */ +var FORUM_LOCAL = 't=76488&p=661300#p661185'; +/** @const */ +var FORUM_CUSTOM = 't=76488&p=749456#p749456'; diff --git a/src/report.js b/src/report.js new file mode 100644 index 0000000..c15a546 --- /dev/null +++ b/src/report.js @@ -0,0 +1,1498 @@ +/* + * report.js -- WME Validator report support + * Copyright (C) 2013-2018 Andriy Berestovskyy + * + * This file is part of WME Validator: https://github.com/WMEValidator/ + * + * WME Validator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * WME Validator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with WME Validator. If not, see . + */ + +/************************************************************************* + * ENCRYPTED FUNCTIONS + *************************************************************************/ + +/** + * Show generated report + */ +_WV.$functions[F_SHOWREPORT] = function (reportFormat) { + // shortcuts + /** @const */ + var _now = new Date(); + /** @const */ + var _nowISO = _now.toISOString().slice(0, 10); + /** @const */ + var _repU = _REP.$users; + /** @const */ + var _repC = _REP.$cities; + /** @const */ + var _repCC = _REP.$cityCounters; + /** @const */ + var _repRC = _REP.$reportCounters; + /** @const */ + var _repS = _REP.$streets; + /** @const */ + var isBeta = -1 !== window.location.href.indexOf("beta"); + + // false if at least one filter or search set + var noFilters = true; + + // final report data + var FR = ''; + var FRheader = ''; + var FRfooter = ''; + + // window object + var newWin = null; + + // layers value + var layersParam = getLayerParam(); + + /////////////////////////////////////////////////////////////////////// + // Format strings + // h1 + var Bh1, Eh1; + // h2 + var Bh2, Eh2; + // small + var Bsmall, Esmall; + // big + var Bbig, Ebig; + // link + var Ba, Ca, Ea; + // link target Validator + var BaV; + // color + var Bcolor, Ccolor, Ecolor; + // bold + var Bb, Eb; + // p + var Bp, Ep; + // br + var Br; + // ol + var Bol, Eol; + // ul + var Bul, Eul; + // li + var Bli, Eli; + // code + var Bcode, Ecode; + // —   + var Mdash, Nbsp; + // current format + var curFormat; + + /////////////////////////////////////////////////////////////////////// + // Support functions + function getLayerParam() { + var mask; + if (WME_BETA) + return W.map.mapState._getLayerVisibilityBitmask(); + + return W.map.mapState.getLayerVisibilityBitmask(); + } + function setFormat(fmt) { + curFormat = fmt; + switch (fmt) { + case RF_HTML: + Bh1 = '\n

    ', Eh1 = '

    \n
    \n'; + Bh2 = '\n\n

    ', Eh2 = '

    \n'; + Bsmall = '', Esmall = ''; + Bbig = '', Ebig = ''; + Ba = '', Ea = ''; + BaV = '' + + '\n' + + '\n' + strTitle + " " + _nowISO + '' + + '\n' + // + '\n' + + '\n' + ; + } + // returns natural list + function getNaturalList(arr) { + if (1 === arr.length) + return arr[0]; + var ret = ''; + arr.forEach(function (e, i) { + if (arr.length - 1 === i) + ret += ' ' + trS("report.and") + ' '; + else if (0 !== i) + ret += ', '; + ret += e; + }); + return ret; + } + // returns document header + function getHeader(strTitle) { + var ret = Bh1 + strTitle + Eh1; + if (RF_LIST !== reportFormat + && RF_CREATEPACK !== reportFormat) { + ret += Bsmall + trS("report.generated.by") + + ' ' + _RT.$curUserName + ' ' + + trS("report.generated.on") + + ' ' + + _nowISO + Esmall + Br + + Br + Bb + + trS("report.source") + + ' ' + Eb + Ba + getTopPermalink() + Ca + + checkNoCity(getReportSource()) + + Ea + Br + ; + + var filters = []; + if (_UI.pMain.pFilter.oExcludeDuplicates.CHECKED) + filters.push(trS("report.filter.duplicate")); + if (_UI.pMain.pFilter.oExcludeStreets.CHECKED) + filters.push(trS("report.filter.streets")); + if (_UI.pMain.pFilter.oExcludeOther.CHECKED) + filters.push(trS("report.filter.other")); + if (_UI.pMain.pFilter.oExcludeNonEditables.CHECKED) + filters.push(trS("report.filter.noneditable")); + if (_UI.pMain.pFilter.oExcludeNotes.CHECKED) + filters.push(trS("report.filter.notes")); + if (filters.length) { + noFilters = false; + ret += Bb + trS("report.filter.title") + + ' ' + Eb + getNaturalList(filters) + + ' ' + + trS("report.filter.excluded") + + Br; + } + filters = []; + if (!_UI.pMain.pSearch.oIncludeYourEdits.NODISPLAY + && _UI.pMain.pSearch.oIncludeYourEdits.CHECKED) + filters.push(trS("report.search.updated.by") + + ' ' + _RT.$curUserName); + if (!_UI.pMain.pSearch.oIncludeUpdatedBy.NODISPLAY + && _UI.pMain.pSearch.oIncludeUpdatedBy.VALUE) + filters.push(trS("report.search.updated.by") + + ' ' + _UI.pMain.pSearch.oIncludeUpdatedBy.VALUE); + if (_UI.pMain.pSearch.oIncludeUpdatedSince.VALUE) + filters.push(trS("report.search.updated.since") + + ' ' + _UI.pMain.pSearch.oIncludeUpdatedSince.VALUE); + if (_UI.pMain.pSearch.oIncludeCityName.VALUE) + filters.push(trS("report.search.city") + + ' ' + _UI.pMain.pSearch.oIncludeCityName.VALUE); + if (_UI.pMain.pSearch.oIncludeChecks.VALUE) + filters.push(trS("report.search.reported") + + ' ' + _UI.pMain.pSearch.oIncludeChecks.VALUE); + if (filters.length) { + noFilters = false; + ret += Bb + trS("report.search.title") + + Eb + ' ' + trS("report.search.only") + + ' ' + getNaturalList(filters) + + ' ' + trS("report.search.included") + Br; + } + + if (isBeta) + ret += Br + Bb + trS("report.beta.warning") + Eb + Br + + trS("report.beta.text") + + Br + Bb + trS("report.beta.share") + Eb + Br + ; + } + return ret; + } + // returns document sub header + function getSubHeader(strTitle) { + return Bh2 + strTitle + Eh2; + } + // returns text representation of access list + function getTextACL(acl) { + if (acl) + return acl.split(',').join(', '); + else + return '*'; + } + // returns check properties + function getCheckProperties(checkID, ccode, showSeverity, showCountry) { + var check = _RT.$checks[checkID]; + var ret = ""; + if (showSeverity && check.SEVERITY && RS_MAX > check.SEVERITY) + ret += Bb + + trS("report.list.severity") + ' ' + Eb + getTextSeverity(check.SEVERITY) + + (check.REPORTONLY ? + " (" + trS("report.list.reportOnly") + ")" : "") + + Br; + if (1 < check.FORLEVEL) + ret += Bb + trS("report.list.forEditors") + + ' ' + Eb + check.FORLEVEL + ' ' + + trS("report.list.andUp") + Br; + if (showCountry) + ret += Bb + trS("report.list.forCountries") + + ' ' + Eb + getTextACL(check.FORCOUNTRY) + Br; + if (check.FORCITY) + ret += Bb + trS("report.list.forCities") + + ' ' + Eb + getTextACL(check.FORCITY) + Br; + + var options; + if (check.OPTIONS && (options = getCheckOptions(checkID, ccode))) { + var defParams = ccode === _I18n.$defLng; + var arrParams = []; + for (var optionName in options) { + // skip options with dots and numbers + if (!/^[a-z]+$/i.test(optionName)) + continue; + var optionTitle = options[optionName + '.title']; + if (defParams && !optionTitle) + continue; + arrParams.push({ + $name: optionName, + $title: optionTitle, + $value: options[optionName] + }); + } + if (arrParams.length) { + ret += Bb; + var country = _I18n.getCapitalizedCountry(ccode) || ccode; + if (defParams) + ret += trS("report.list.params"); + else + ret += trSO("report.list.params.set", { "country": country }); + ret += Eb + Bcode + '"' + checkID + '.params": {\n'; + + for (var i = 0; i < arrParams.length; i++) { + var param = arrParams[i]; + if (defParams) + ret += ' // ' + param.$title + '\n'; + ret += ' "' + param.$name + '": ' + + JSON.stringify(param.$value) + ',' + '\n'; + } + ret += "}," + Ecode; + } + } + return ret; + } + function addTextLabels(pack, label, defSet, oldPack) { + var defData = (defSet[label] || '') + .replace(new RegExp('^W:'), PFX_WIKI) + .replace(new RegExp('^F:'), PFX_FORUM) + ; + var origData = (oldPack[label] || ''); + if (origData) { + var oldData = origData + .replace(new RegExp('^' + GL_TODOMARKER), '') + .replace(new RegExp('^W:'), PFX_WIKI) + .replace(new RegExp('^F:'), PFX_FORUM) + ; + // preserve old data + var oldDataEN = (oldPack[label + '.en'] || '') + .replace(new RegExp('^W:'), PFX_WIKI) + .replace(new RegExp('^F:'), PFX_FORUM) + ; + if (oldDataEN) { + if (oldDataEN === defData) { + // no changes + pack[label + '.en'] = defData; + pack[label] = origData; + } + else { + // new default string + pack[label + '.en'] = defData; + pack[label] = GL_TODOMARKER + oldData; + } + } + else { + // no english data + // we assume no changes + pack[label + '.en'] = defData; + pack[label] = origData; + } + } + else { + // no old data + // mark line for translation + pack[label + '.en'] = defData; + pack[label] = GL_TODOMARKER + defData; + } + } + // returns new pack header + function getPackHeader(country, lng) { + return '// ==UserScript==' + Br + + '// @name ' + WV_NAME + ' Localization for ' + country + Br + + '// @version ' + WV_VERSION + Br + + '// @description This script localizes ' + WV_NAME + ' for ' + country + + '. You also need main package (' + WV_NAME + ') installed.' + Br + + '// @match https://editor-beta.waze.com/*editor/*' + Br + + '// @match https://www.waze.com/*editor/*' + Br + + '// @grant none' + Br + + '// @run-at document-start' + Br + + '// ==/UserScript==' + Br + + '//' + Br + + '/*' + Br + + (lng ? + ' Please translate all the lines marked with "' + GL_TODOMARKER + '"' + Br + + ' Please DO NOT change ".en" properties. To override english text use "titleEN",' + Br + + ' "problemEN" and "solutionEN" properties (see an example below).' + Br + + Br + : '') + + ' See Settings->About->Available checks for complete list of checks and their params.' + Br + + Br + + ' Examples:' + Br + + Br + + ' Enable #170 "Lowercase street name" but allow lowercase "exit" and "to":' + Br + + ' "170.enabled": true,' + Br + + ' "170.params": {' + Br + + ' "regexp": "/^((exit|to) )?[a-z]/",' + Br + + ' "},' + Br + + Br + + ' Enable #130 "Custom check" to find a dot in street names, but allow dots at Ramps:' + Br + + ' "130.enabled": true,' + Br + + ' "130.params": {' + Br + + ' "titleEN": "Street name with a dot",' + Br + + ' "problemEN": "There is a dot in the street name (excluding Ramps)",' + Br + + ' "solutionEN": "Expand the abbreviation or remove the dot",' + Br + + ' "template": "${type}:${street}",' + Br + + ' "regexp": "D/^[^4][0-9]?:.*\\\\./",' + Br + + ' },' + Br + + ' *Note: use D at the beginning of RegExp to enable debugging on JS console.' + Br + + ' *Note: do not forget to escape backslashes in strings, i.e. use "\\\\" instead of "\\".' + Br + + '*/' + Br + ; + } + // returns new pack + function getPack(country, ccode, lng) { + var ucountry = country.toUpperCase(); + var _country = country.split(' ').join('_'); + var oldPack = _I18n.$translations[ccode] || {}; + + var ret = '' + + Br + + 'window.' + WV_NAME_ + '_' + _country + ' = ' + ; + + var newCountries = []; + for (var k in _I18n.$country2code) { + if (ccode === _I18n.$country2code[k] + && ucountry !== k) + newCountries.push(_I18n.capitalize(k)); + } + // add current country to the top of the list + newCountries.unshift(country); + + // add current user to authors + var newAuthor = oldPack[".author"] || _RT.$topUser.$userName; + if (-1 === newAuthor.indexOf(_RT.$topUser.$userName)) + newAuthor += " and " + _RT.$topUser.$userName; + + var newLink = oldPack[".link"] || GL_TODOMARKER; + + var pack = { + ".country": (1 === newCountries.length ? + newCountries[0] + : newCountries), + ".codeISO": ccode, + + ".author": newAuthor, + ".updated": _nowISO, + ".link": newLink, + }; + if (ccode in _I18n.$code2code) + pack[".fallbackCode"] = _I18n.$code2code[ccode]; + + if (lng) { + if (ccode in _I18n.$code2dir) + pack[".dir"] = _I18n.$code2dir[ccode]; + + var newLngs = []; + for (var k in _I18n.$lng2code) { + if (ccode === _I18n.$lng2code[k] + && k !== lng) + newLngs.push(k); + } + // add current lng to the top of the list + newLngs.unshift(lng); + pack[".lng"] = (1 === newLngs.length ? + newLngs[0] + : newLngs); + } + + // compare and add UI strings + if (lng) { + for (var label in _I18n.$defSet) { + // skip meta labels and checks + if (/^\./.test(label) + || /^[0-9]/.test(label)) + continue; + + // get data + addTextLabels(pack, label, _I18n.$defSet, oldPack); + } + } + + // compare and add checks + var allLabels = _RT.$otherLabels.concat(_RT.$textLabels); + var arrDepCodes = _I18n.getDependantCodes(ccode); + for (var i = 1; i < MAX_CHECKS; i++) { + // skip mirror checks + if ((CK_MIRRORFIRST + 100) <= i + && (CK_MIRRORLAST + 100) >= i) + continue; + // check is enabled? + var label = i + '.enabled'; + + var checkEnabled = false; + if (_I18n.$defSet[label] || oldPack[label]) + checkEnabled = true; + if (!checkEnabled) { + // check dependant countries + for (var depC = 0; depC < arrDepCodes.length; depC++) { + var depCode = arrDepCodes[depC]; + if (_I18n.$translations[depCode] + && _I18n.$translations[depCode][label]) + checkEnabled = true; + } + } + // check if check is no longer exist + if (checkEnabled && !((i + '.title') in _I18n.$defSet)) { + pack[i + '.note'] = GL_TODOMARKER + "The check #" + i + " is no longer exist. See the forum thread for more details."; + continue; + } + + for (var j = 0; j < allLabels.length; j++) { + var labelSfx = allLabels[j]; + label = i + '.' + labelSfx; + + var defData = _I18n.$defSet[label]; + var oldData = oldPack[label]; + + if (classCodeDefined(defData) || classCodeDefined(oldData)) { + if (-1 !== _RT.$textLabels.indexOf(labelSfx)) { + // text label + if (lng && checkEnabled) + addTextLabels(pack, label, _I18n.$defSet, oldPack); + } + else { + // copy and clear compiled params + if ("params" === labelSfx) { + if (!classCodeDefined(oldData)) + continue; + defData = deepCopy(defData || {}); + oldData = deepCopy(oldData); + for (var k = CO_MIN; k <= CO_MAX; k++) { + delete defData[k]; + delete oldData[k]; + } + for (var k in defData) { + if (!defData.hasOwnProperty(k)) continue; + if (/\.title$/.test(k)) + delete defData[k]; + } + } + + // non-text label + if (!deepCompare(defData, oldData)) + pack[label] = oldData; + } + } + } + } + + ret += JSON.stringify(pack, null, ' ') + ';\n'; + + return ret; + } + + // returns list of checks + function getListOfChecks(countryID, country) { + var ucountry = country.toUpperCase(); + var ccode = ""; + if (countryID) + ccode = _I18n.getCountryCode(ucountry); + + var ret = trS("report.list.see") + ' ' + Bb + + trS("report.list.checks") + Eb + Br + Br; + + var fallbacks = ''; + if (ccode) + for (var i in _I18n.$country2code) { + if (!_I18n.$country2code.hasOwnProperty(i)) continue; + + if (i === ucountry) + continue; + + var acode = _I18n.$country2code[i]; + if (ccode && acode !== ccode) + continue; + + fallbacks += _I18n.capitalize(i) + + ' \u2192 ' + + country + Br; + } + for (var i in _I18n.$code2code) { + if (!_I18n.$code2code.hasOwnProperty(i)) continue; + + var countryFrom = _I18n.getCapitalizedCountry(i); + var countryTo = _I18n.getCapitalizedCountry(_I18n.$code2code[i]); + if (ccode && i !== ccode && _I18n.$code2code[i] !== ccode) + continue; + if (country && countryFrom !== country && countryTo !== country) + continue; + fallbacks += countryFrom + ' (' + i + ') \u2192 ' + + countryTo + + ' (' + _I18n.$code2code[i] + ')' + Br; + } + if (fallbacks) + ret += Bb + trS("report.list.fallback") + Eb + Br + + fallbacks; + + var sortedIDs = getSortedCheckIDs(); + if (ccode) { + // country + var enabledIDs = []; + var disabledIDs = []; + + // check if enabled for country + sortedIDs.forEach(function (cid) { + var c = _RT.$checks[cid]; + if (!c) return; + // do not show global access + if (RS_MAX === c.SEVERITY) return; + + var en = true; + var forCountry = c.FORCOUNTRY; + if (forCountry) { + if (!_WV.checkAccessFor(forCountry, function (e) { + if (e in _I18n.$code2country) + return _I18n.$code2country[e] === ucountry; + error("Please report: fc=" + e); + return false; + })) + en = false; + } + if (en) + enabledIDs.push(cid); + else + disabledIDs.push(cid); + }); + + ret += Bh2 + trSO("report.list.enabled", { "n": enabledIDs.length }) + + ' ' + country + ":" + Eh2 + Bul; + enabledIDs.forEach(function (cid) { + ret += Bli + getCheckDescription(cid, countryID, Bb, Eb + Br) + + Bsmall; + ret += getCheckProperties(cid, ccode, false, false); + ret += Esmall + Eli; + }); + ret += Eul; + + ret += Bh2 + trSO("report.list.disabled", { "n": disabledIDs.length }) + + ' ' + country + ":" + Eh2 + Bul; + disabledIDs.forEach(function (cid) { + ret += Bli + getCheckDescription(cid, 0, Bb, Eb + Br) + Bsmall; + ret += getCheckProperties(cid, _I18n.$defLng, false, true); + ret += Esmall + Eli; + }); + ret += Eul; + } + else { + // no country + ret += Bh2 + trSO("report.list.total", { "n": sortedIDs.length }) + ':' + + Eh2 + Bul; + sortedIDs.forEach(function (cid) { + var c = _RT.$checks[cid]; + if (!c) return; + // do not show global access + if (RS_MAX === c.SEVERITY) return; + + ret += Bli + getCheckDescription(cid, 0, Bb, Eb + Br) + Bsmall; + ret += getCheckProperties(cid, _I18n.$defLng, false, true); + ret += Esmall + Eli; + }); + ret += Eul; + } + + return ret; + } + // returns HTML footer + function getHTMLFooter() { + return '\n
    ' + + '\n
    ' + WV_NAME + ' v' + WV_VERSION + '
    © 2013-2016 berestovskyy
    ' + + '\n' + ; + } + // returns text area header + function getTAHeader(h) { + var ret = + '\n

    ' + + (RF_CREATEPACK === reportFormat ? + trS("msg.textarea.pack") + : trS("msg.textarea")) + + ':

    ' + + '\n

    '; + } + // returns size warning + function getSizeWarning(size) { + return 5e4 < size ? + '\n

    ' + + trSO("report.size.warning", { "n": size }) + + '

    ' + : ''; + } + // opens new browser window + function openWindow(data) { + UW.open("data:text/html;charset=UTF-8," + encodeURIComponent(data), + "_blank"); + } + // opens new browser window for final report + /** @param {string=} title */ + function openWindowFR(title) { + var encFR = "data:text/html;charset=UTF-8,"; + if (newWin) { + // insert save button + if (reportFormat === RF_HTML) { + title = title.split(" ").join("_"); + newWin.document.write(FRheader); + var saveRep = FRheader; + saveRep += FR; + saveRep += FRfooter; + saveRep = encodeURIComponent(saveRep); + var saveLink = '

    '; + newWin.document.write(saveLink); + newWin.document.write(FR); + newWin.document.write(saveLink); + newWin.document.write(FRfooter); + } + else + newWin.document.write(FR); + } + else { + encFR += encodeURIComponent(FRheader); + FRheader = ''; + encFR += encodeURIComponent(FR); + FR = ''; + encFR += encodeURIComponent(FRfooter); + FRfooter = ''; + UW.open(encFR, "_blank"); + } + } + // filter helpers + var seenSegments = {}; + var lastCheckID = -1; + var lastCityID = -1; + var lastStreetID = -1; + var counterNotes = 0; + var counterWarnings = 0; + var counterErrors = 0; + var counterCustoms1 = 0; + var counterCustoms2 = 0; + // reset filter + function resetFilter() { + seenSegments = {}; + lastCheckID = -1; + lastCityID = -1; + lastStreetID = -1; + counterNotes = 0; + counterWarnings = 0; + counterErrors = 0; + counterCustoms1 = 0; + counterCustoms2 = 0; + } + // returns TOC + function getTOC() { + resetFilter(); + FR += '\n
    '; + FR += '\n'; + FR += trS("report.contents"); + FR += ''; + FR += '\n
      '; + traverseReport(function (obj) { + if (checkFilter(0, obj.$segmentCopy, seenSegments) + && getFilteredSeverity(obj.$check.SEVERITY, obj.$checkID, false)) { + if (obj.$checkID !== lastCheckID) { + lastCheckID = obj.$checkID; + var check = obj.$check; + + var strCountry = _REP.$countries[obj.$segmentCopy.$countryID]; + var ccode = ""; + + if (strCountry) + ccode = _I18n.getCountryCode(strCountry.toUpperCase()); + else { + // try top country + ccode = _RT.$cachedTopCCode; + } + var options = trO(check.OPTIONS, ccode) + FR += '\n
    1. '; + FR += exSOS(check.TITLE, options, "titleEN"); + FR += '
    2. '; + } + // TODO: + // bug: TOC item shows duplicate segments + // solution: filter, then create the toc and report + + // alt solution: remove dublicate checks! + // return RT_NEXTCHECK; + } + }); + + FR += '\n
    3. '; + FR += trS("report.summary"); + FR += '
    4. '; + FR += '\n
    \n
    '; + } + // get sorted check IDs + function getSortedCheckIDs() { + return _RT.$sortedCheckIDs ? _RT.$sortedCheckIDs + : _RT.$sortedCheckIDs = + Object.keys(_RT.$checks) + .sort(cmpCheckIDs); + } + // _REP->$cityIDs->streetIDs->$segmentIDs->$reportIDs + // traverse report and call a handler + function traverseReport(handler) { + var mapCenter = WM.getCenter(); + + // get sorted cities + function getSortedCities() { + var ret = _REP.$sortedCityIDs; + if (!ret || ret.length != _REP.$unsortedCityIDs.length) + return _REP.$sortedCityIDs = + [].concat(_REP.$unsortedCityIDs).sort(function (a, b) { + return _repC[a].localeCompare(_repC[b]); + }); + return ret; + } + // get sorted streets + function getSortedStreets(repC) { + var ret = repC.$sortedStreetIDs; + if (!ret || ret.length != repC.$unsortedStreetIDs.length) + return repC.$sortedStreetIDs + = [].concat(repC.$unsortedStreetIDs).sort(function (a, b) { + return _repS[a].localeCompare(_repS[b]); + }); + return ret; + } + // get hypot + function getHypot(c1, c2) { + // return Math.round(Math.sqrt(c1*c1 + c2*c2)*10); + return Math.sqrt(c1 * c1 + c2 * c2); + } + // get sorted segments + function getSortedSegments(repS) { + var ret = repS.$sortedSegmentIDs; + var repSeg = repS.$segmentIDs; + if (!ret || ret.length != repS.$unsortedSegmentIDs.length) + return repS.$sortedSegmentIDs + = [].concat(repS.$unsortedSegmentIDs).sort(function (a, b) { + var segA = repSeg[a], segB = repSeg[b]; + if (segA.$typeRank !== segB.$typeRank) + return segB.$typeRank - segA.$typeRank; + // if ranks are the same - sort by the distance + /** @const */ + var distAB = getHypot(segA.$center.lat - segB.$center.lat, + segA.$center.lon - segB.$center.lon); + // the segments are close + if (0.002 > distAB) return 0; + /** @const */ + var distA = getHypot(mapCenter.lat - segA.$center.lat, + mapCenter.lon - segA.$center.lon); + /** @const */ + var distB = getHypot(mapCenter.lat - segB.$center.lat, + mapCenter.lon - segB.$center.lon); + return distA - distB; + }); + return ret; + } + + /////////////////////////////////////////////////////////////////// + // For all sorted checks + var checkIDs = getSortedCheckIDs(); + nextCheck: for (var i = 1; i < checkIDs.length; i++) { + var checkID = checkIDs[i]; + var check = _RT.$checks[checkID]; + if (!check) continue; + + // filter minor issues + if (_UI.pMain.pFilter.oExcludeNotes.CHECKED + && RS_NOTE === check.SEVERITY + ) + continue; + + // for all cities + var sortedCities = getSortedCities(); + for (var sorcid = 0; sorcid < sortedCities.length; sorcid++) { + var cid = sortedCities[sorcid]; + var repC = _REP.$cityIDs[cid]; + + // for all streets + var sortedStreets = getSortedStreets(repC); + for (var sorsid = 0; sorsid < sortedStreets.length; sorsid++) { + var sid = sortedStreets[sorsid]; + var repS = repC.$streetIDs[sid]; + + // for all segment copies + var sortedSegments = getSortedSegments(repS); + for (var sorscid = 0; sorscid < sortedSegments.length; sorscid++) { + var scid = sortedSegments[sorscid]; + var sc = repS.$segmentIDs[scid]; + if (checkID in sc.$reportIDs) { + var obj = { + $checkID: checkID, + $check: check, + $param: sc.$reportIDs[checkID], + $cityParam: repC.$params[checkID], + $streetParam: repS.$params[checkID], + $segmentCopy: sc + }; + switch (handler(obj)) { + case RT_STOP: + return; + case RT_NEXTCHECK: + continue nextCheck; + }; + } // checkID in reportIDs + } // for all segmets + } // for all streets + } // for all cities + } // for all checks + } + + // closes report street + function closeReportStreet() { + if (0 <= lastStreetID || 0 <= lastCityID) { + lastStreetID = -1; + FR += Eli; + } + } + // closes report city + function closeReportCity() { + if (0 <= lastCityID) { + lastCityID = -1; + closeReportStreet(); + FR += Eul; + } + } + // closes report check + function closeReportCheck() { + if (0 <= lastCheckID) { + if (LIMIT_PERCHECK < _repRC[lastCheckID]) { + if (1 === _repRC[lastCheckID] - LIMIT_PERCHECK) + FR += '[and more...]'; + else { + FR += '[and '; + FR += (_repRC[lastCheckID] - LIMIT_PERCHECK); + FR += ' segments more...]'; + } + } + lastCheckID = -1; + closeReportCity(); + } + if (RF_HTML === curFormat) + FR += ''; + } + // returns check description + function getCheckDescription(checkID, countryID, headB, headE) { + var check = _RT.$checks[checkID]; + var ret = headB; + var strCountry = _REP.$countries[countryID]; + var ccode = ""; + + if (strCountry) + ccode = _I18n.getCountryCode(strCountry.toUpperCase()); + else { + // try top country + ccode = _RT.$cachedTopCCode; + } + var options = trO(check.OPTIONS, ccode); + + if (check.COLOR) + ret += Bcolor + check.COLOR + Ccolor + '\u2588\u2588 ' + Ecolor; + ret += '' + + (countryID ? exSOS(check.TITLE, options, "titleEN") : check.TITLE) + + ' (#' + checkID + ')' + + headE; + + var sevColor = GL_NOTECOLOR; + switch (check.SEVERITY) { + case RS_WARNING: + sevColor = GL_WARNINGCOLOR; + break; + case RS_ERROR: + sevColor = GL_ERRORCOLOR; + break; + case RS_CUSTOM1: + sevColor = GL_CUSTOM1COLOR; + break; + case RS_CUSTOM2: + sevColor = GL_CUSTOM2COLOR; + break; + } + + if (check.PROBLEM) { + ret += Bcolor + sevColor + Ccolor + + (countryID ? exSOS(check.PROBLEM, options, "problemEN") + : check.PROBLEM); + var pl = trO(check.PROBLEMLINK, ccode); + if (pl) { + ret += ': ' + Ba + pl + Ca + + trO(check.PROBLEMLINKTEXT, ccode) + Ea; + } + else + ret += '.'; + + ret += Ecolor + Br; + } + + if (check.SOLUTION) { + ret += (countryID ? exSOS(check.SOLUTION, options, "solutionEN") + : check.SOLUTION); + var sl = trO(check.SOLUTIONLINK, ccode); + if (sl) { + ret += ': ' + Ba + sl + Ca + + trO(check.SOLUTIONLINKTEXT, ccode) + Ea; + } + else + ret += '.'; + + ret += Br; + } + + return ret; + } + // returns report check + function getReportCheck(obj) { + closeReportCheck(); + + if (RF_HTML === curFormat) { + FR += '
    '; + } + + FR += getCheckDescription(obj.$checkID, obj.$segmentCopy.$countryID, + Bh2, Eh2); + FR += Br; + } + // returns report city + function getReportCity(obj) { + closeReportCity(); + FR += Bbig; + FR += Bb; + FR += checkNoCity(_repC[obj.$segmentCopy.$cityID]); + FR += Eb; + FR += Ebig; + if (obj.$cityParam) { + FR += Mdash; + FR += obj.$cityParam; + } + FR += Bul; + } + // returns report street + function getReportStreet(obj) { + closeReportStreet(); + FR += Bli; + FR += checkNoStreet(_repS[obj.$segmentCopy.$streetID]); + FR += ', '; + FR += checkNoCity(_repC[obj.$segmentCopy.$cityID]); + if (obj.$streetParam) { + FR += Mdash; + FR += obj.$streetParam; + } + FR += Br; + } + + // returns permalink + function getPermalink(obj) { + var z = SCAN_ZOOM; + if (50 > obj.$segmentCopy.$length) + z = 7; + else if (500 > obj.$segmentCopy.$length) { + if (6 > z) z += 1; + } + else + z = 4; + FR += window.location.origin; + FR += window.location.pathname; + FR += '?zoom='; + FR += z; + FR += '&lat='; + FR += obj.$segmentCopy.$center.lat; + FR += '&lon='; + FR += obj.$segmentCopy.$center.lon; + FR += '&layers='; + FR += layersParam; + FR += '&env='; + FR += nW.location.code; + FR += '&segments='; + FR += obj.$segmentCopy.$segmentID; + } + // report item handler + function getReportItem(obj) { + if (!checkFilter(0, obj.$segmentCopy, seenSegments) + || !getFilteredSeverity(obj.$check.SEVERITY, obj.$checkID, false)) + return; + + // update max severity + if (_REP.$maxSeverity < obj.$check.SEVERITY) + _REP.$maxSeverity = obj.$check.SEVERITY; + + if (obj.$checkID !== lastCheckID) { + getReportCheck(obj); + if (noFilters) { + var c = _repRC[obj.$checkID]; + switch (obj.$check.SEVERITY) { + case RS_NOTE: + counterNotes += c; + break; + case RS_WARNING: + counterWarnings += c; + break; + case RS_ERROR: + counterErrors += c; + break; + case RS_CUSTOM1: + counterCustoms1 += c; + break; + case RS_CUSTOM2: + counterCustoms2 += c; + break; + } + } + } + if (obj.$segmentCopy.$cityID !== lastCityID) + getReportCity(obj); + if (obj.$segmentCopy.$streetID !== lastStreetID) + getReportStreet(obj); + lastCheckID = obj.$checkID; + lastCityID = obj.$segmentCopy.$cityID; + lastStreetID = obj.$segmentCopy.$streetID; + + if (!noFilters) { + switch (obj.$check.SEVERITY) { + case RS_NOTE: + counterNotes++; + break; + case RS_WARNING: + counterWarnings++; + break; + case RS_ERROR: + counterErrors++; + break; + case RS_CUSTOM1: + counterCustoms1++; + break; + case RS_CUSTOM2: + counterCustoms2++; + break; + } + } + + FR += BaV; + getPermalink(obj); + FR += Ca + if (isBeta) FR += 'B:'; + FR += obj.$segmentCopy.$segmentID; + FR += Ea; + FR += ' '; + } + // returns total counters + function getSummary() { + if (RF_HTML === curFormat) + FR += ''; + + FR += Bh2; + FR += trS("report.summary"); + FR += Eh2; + FR += Bb; + FR += trS("report.segments"); + FR += Eb; + FR += ' '; + FR += _REP.$counterTotal; + FR += Br; + if (counterCustoms1 || counterCustoms2) { + FR += Bb; + FR += trS("report.customs"); + FR += Eb; + FR += ' '; + FR += counterCustoms1; + FR += '/'; + FR += counterCustoms2; + if (_REP.$isLimitPerCheck) + FR += '*'; + FR += Br; + } + + FR += Bb; + FR += trS("report.reported"); + FR += Eb; + FR += ' '; + var summary = []; + if (counterErrors) + summary.push(Bb + trS("report.errors") + Mdash + Eb + ' ' + + counterErrors + + (_REP.$isLimitPerCheck ? '*' : '') + + ' (' + Math.round(counterErrors * 1e3 / _REP.$counterTotal) + + '\u2030)' + ); + if (counterWarnings) + summary.push(Bb + trS("report.warnings") + Mdash + Eb + ' ' + + counterWarnings + + (_REP.$isLimitPerCheck ? '*' : '') + + ' (' + Math.round(counterWarnings * 1e3 / _REP.$counterTotal) + + '\u2030)' + ); + if (counterNotes) + summary.push(Bb + trS("report.notes") + Mdash + Eb + ' ' + counterNotes + + (_REP.$isLimitPerCheck ? '*' : '') + ); + + FR += getNaturalList(summary); + FR += Br; + + if (_REP.$isLimitPerCheck) { + FR += trS("report.note.limit"); + FR += Br; + } + + FR += Br; + FR += trS("report.forum"); + FR += ' '; + FR += Ba; + FR += PFX_FORUM; + FR += FORUM_HOME; + FR += Ca; + FR += trS("report.forum.link"); + FR += Ea; + FR += Br; + FR += Br; + FR += trS("report.thanks"); + FR += Br; + } + // returns report + function getReport(fmt) { + var oldFormat = curFormat; + setFormat(fmt); + + resetFilter(); + _REP.$maxSeverity = 0; + + traverseReport(function (e) { + getReportItem(e) + }); + + closeReportCheck(); + getSummary(); + + setFormat(oldFormat); + } + // update max severity + function updateMaxSeverity() { + resetFilter(); + _REP.$maxSeverity = 0; + + traverseReport(function (obj) { + if (checkFilter(0, obj.$segmentCopy, seenSegments) + && getFilteredSeverity(obj.$check.SEVERITY, obj.$checkID, false)) { + if (_REP.$maxSeverity < obj.$check.SEVERITY) + _REP.$maxSeverity = obj.$check.SEVERITY; + if (_RT.$curMaxSeverity === _REP.$maxSeverity) + return RT_STOP; + } + }); + } + + // prepare report + setFormat(RF_HTML); + var t = trS("report.title"); + switch (reportFormat) { + case RF_UPDATEMAXSEVERITY: + updateMaxSeverity(); + break; + case RF_CREATEPACK: + var wType = "Localization Package Wizard"; + if (!window.confirm(getMsg(wType, + "\nBefore starting the Wizard:" + + "\n\n1. Position WME over your country" + + "\n so the Wizard will know your country name" + + "\n\n2. Switch WME to your language" + + "\n so the Wizard will add translations into the package" + + "\n\n3. Enable any previous version of localization pack" + + "\n so the Wizard will preserve already translated text" + , + true))) + break; + // prompt for country + var country = _RT.$topCity && _RT.$topCity.$country ? + _RT.$topCity.$country + : window.prompt( + getMsg(wType, + "\nWME country name (example: United Kingdom):", true)) + ; + if (!country) + break; + var ucountry = country.toUpperCase(); + + // prompt for country code + var ccode = _I18n.getCountryCode(ucountry) ? + _I18n.getCountryCode(ucountry) + : window.prompt( + getMsg(wType, + "\nISO 3166-1 Alpha-2 country code (example: UK):", true)); + ; + if (!ccode) + break; + ccode = ccode.toUpperCase(); + + // prompt for language + var lng = window.prompt( + getMsg(wType, + "\nPlease confirm the WME language code:" + + "\n\nfor \"EN\" no translations will be included into the package" + + "\nfor any other code the translations will be included" + , + true), + _RT.$lng); + if (!lng) + break; + lng = lng.toUpperCase(); + + if (_I18n.$defLng === lng) + t = "Minimal Localization for " + country; + else + t = "Localization and Translation for " + country; + + if (_I18n.$defLng === lng) + lng = ''; + + var lPack = + getHTMLHeader(t) + + getHeader(t) + + getTAHeader('400px') + + getPackHeader(country, lng) + + getPack(country, ccode, lng) + ; + // generate dependants + var arrDepCodes = _I18n.getDependantCodes(ccode); + for (var i = 0; i < arrDepCodes.length; i++) { + var depCode = arrDepCodes[i]; + var depCountry = _I18n.getCapitalizedCountry(depCode); + if (depCountry && depCode) { + lPack += '\n// Dependant package:'; + lPack += getPack(depCountry, depCode, ''); + } + } + + openWindow( + lPack + + getTAFooter() + + getHTMLFooter() + ); + break; + case RF_LIST: + var countryID = 0; + var country = ""; + t = trS("report.list.title") + ' '; + // try top city + if (_RT.$topCity && _RT.$topCity.$country) { + countryID = _RT.$topCity.$countryID; + country = _RT.$topCity.$country; + t += country + " (v" + WV_VERSION + ")"; + } + else + t += "v" + WV_VERSION; + openWindow( + getHTMLHeader(t) + + getHeader(t) + + getListOfChecks(countryID, country) + + getTAHeader('200px') + + getHeader(t) + + getListOfChecks(countryID, country) + + getTAFooter() + + getHTMLFooter() + ); + break; + case RF_HTML: + // open new window + if (-1 !== window.navigator.vendor.indexOf("Google")) + newWin = UW.open("", "_blank"); + FR += getHTMLHeader(t); + FR += getHeader(t); + // save header to insert save button + FRheader = FR; + FR = ''; + getTOC(); + getReport(RF_HTML); + // ignore empty reports + if (0 === counterNotes + counterWarnings + counterErrors + + counterCustoms1 + counterCustoms2) { + if (newWin) + newWin.close(); + async(F_UPDATEUI); + break; + } + FRfooter += getHTMLFooter(); + openWindowFR(t); + break; + case RF_BB: + // open new window + if (-1 !== window.navigator.vendor.indexOf("Google")) + newWin = UW.open("", "_blank"); + + var tf = t + " " + trS("report.share"); + FR += getHTMLHeader(tf); + FR += getHeader(tf); + FR += getTAHeader('200px'); + var beforeShareLen = FR.length; + FR += getHeader(t); + getReport(RF_BB); + var shareLen = FR.length - beforeShareLen; + + // ignore empty reports + if (0 === counterNotes + counterWarnings + counterErrors + + counterCustoms1 + counterCustoms2) { + if (newWin) + newWin.close(); + // updateMaxSeverity(); + async(F_UPDATEUI); + break; + } + FR += getTAFooter(); + FR += getSizeWarning(shareLen); + FR += getHTMLFooter(); + openWindowFR(); + break; + } + + resetFilter(); +} diff --git a/src/validate.js b/src/validate.js new file mode 100644 index 0000000..a1c2bc2 --- /dev/null +++ b/src/validate.js @@ -0,0 +1,3016 @@ +/* + * validate.js -- WME Validator main functionality + * Copyright (C) 2013-2018 Andriy Berestovskyy + * + * This file is part of WME Validator: https://github.com/WMEValidator/ + * + * WME Validator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * WME Validator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with WME Validator. If not, see . + */ + +/************************************************************************* + * ENCRYPTED FUNCTIONS + *************************************************************************/ + +/** + * Validate current view + */ +_WV.$functions[F_VALIDATE] = function (disabledHL) { + if (!_RT.$isMapChanged) + return; + _RT.$isMapChanged = false; + + // global update severity flag + var bUpdateMaxSeverity = false; + + if (RTStateIs(ST_RUN)) + beep(10); + + var options; + var skippedSegment = false; + + if (disabledHL) { + updateSegmentProperties([], true); + return; + } + if (LIMIT_TOTAL < _REP.$counterTotal && !isErrorFlag()) { + setErrorFlag(); + if (RTStateIs(ST_RUN)) { + window.alert(getMsg( + trS("msg.autopaused"), + "\n" + trS("msg.limit.segments") + + trS("msg.limit.segments.continue"), + true)); + // pause scanning + sync(F_PAUSE); + } + else + warning(trS("msg.limit.segments") + + trS("msg.limit.segments.clear")); + + return; + } + + /////////////////////////////////////////////////////////////////////// + // 0. Prepare objects + + // update map info + _RT.$topCenter = WM.getCenter(); + + // 1. Dispatch per view WHC event + if (_UI.pSettings.pScanner.oReportExt.CHECKED + && _RT.oReportWMECH.CHECKED) { + var el = document.getElementById(_RT.oReportWMECH.FORID); + if (el) { + var ev = new CustomEvent("click"); + el.dispatchEvent(ev); + } + } + + // 3. TODO: Loop over the segments & run per segment tools + // try + // { + // sync(F_CHECKSEGMENTS); + // } + // catch(e) {log("ERROR: " + e)} + + + // Shortcuts + /** @const */ + var _repC = _REP.$cities; + /** @const */ + var _repCC = _REP.$cityCounters; + /** @const */ + var _repRC = _REP.$reportCounters; + /** @const */ + var _repS = _REP.$streets; + /** @const */ + var _repU = _REP.$users; + + /////////////////////////////////////////////////////////////////////// + // Support Functions + /** + * Check limit + */ + function isLimitOk(id) { + if (DEF_DEBUG) { + return true; + } + else + return !(LIMIT_PERCHECK < _repRC[id]); + } + /** + * Format date + * @returns {string} + * @param {string} d + */ + function formatDate(d) { var n = new Date(d); return n.toISOString().substr(0, 10); } + + /** + * Get user name + * @returns {string} + * @param {number} objID + */ + function getUserName(objID) { + var u = WMo.users.get(objID); + return u ? u.userName : objID.toString(); + } + + /** + * Get user level + * @returns {number} + * @param {number} objID + */ + function getUserLevel(objID) { + var u = WMo.users.get(objID); + return u ? u.normalizedLevel : 0; + } + + /** + * Simple node object constructor + * @constructor + * @struct + * @param {number} objID + * @param {number} segID + */ + function SimpleNODE(objID, segID) { + // cached node + /** @type {Waze.NODE} */ + this.$rawNode = null; + /** @type {number} */ + this.$nodeID = objID; + // cached center + /** @type {OpenLayers.LonLat} */ + this._center = null; + this.$center = null; + /** @type {boolean} */ + this.$isUturn = false; + /** @type {boolean} */ + this.$isEditable = true; + /** @type {boolean} */ + this.$isPartial = true; + /** @type {Array} */ + this._rawRestrictions = []; + /** @type {Array} */ + this._rawRestrictionIDs = []; + /** @type {Array.} */ + this._restrictions = null; + /** @type {Array.} */ + this.$restrictions = null; + /** @type {Array} */ + this._rawOtherSegments = []; + /** @type {Array} */ + this._otherSegments = null; + /** @type {Array} */ + this.$otherSegments = null; + /** @type {Array} */ + this._rawOutConnections = []; + /** @type {Array} */ + this._outConnections = null; + /** @type {Array} */ + this.$outConnections = null; + /** @type {Array} */ + this._rawInConnections = []; + /** @type {Array} */ + this._inConnections = null; + /** @type {Array} */ + this.$inConnections = null; + /** @type {number} */ + this.$restrictionsLen = 0; + /** @type {number} */ + this.$otherSegmentsLen = 0; + /** @type {number} */ + this.$outConnectionsLen = 0; + /** @type {number} */ + this.$inConnectionsLen = 0; + + var n = WMo.nodes.get(objID); + this.$rawNode = n; + if (n) { + this.$isPartial = n.attributes.partial; + this.$isEditable = n.areConnectionsEditable(); + // convert restrictions into an array + var co = n.attributes.restrictions; + for (var k in co) { + if (!co[k]) + continue; + var _con = k.split(','); + var con0 = +_con[0]; + if (segID === con0) { + var con1 = +_con[1]; + var cok = co[k]; + for (var j = 0, l = cok.length; j < l; j++) { + this._rawRestrictions.push(cok[j]); + this._rawRestrictionIDs.push(con1); + } + } + + } + this.$restrictionsLen = this._rawRestrictions.length; + // convert segIDs into an array + for (var i = 0; i < n.attributes.segIDs.length; i++) { + var si = n.attributes.segIDs[i]; + // TODO: workaround for hangs at new segment save / 20150105 + if (segID === si || !WMo.segments.get(si)) + continue; + this._rawOtherSegments.push(si); + } + this.$otherSegmentsLen = this._rawOtherSegments.length; + // convert connections into in/out arrays + co = n.attributes.connections; + for (var k in co) { + if (!co[k]) + continue; + var _con = k.split(','); + var con0 = +_con[0]; + var con1 = +_con[1]; + + if (segID === con0 && segID === con1) { + this.$isUturn = true; + continue; + } + // out connection + if (segID === con0) + this._rawOutConnections.push(con1); + // in connection + if (segID === con1) + this._rawInConnections.push(con0); + } + } + this.$outConnectionsLen = this._rawOutConnections.length; + this.$inConnectionsLen = this._rawInConnections.length; + + Object.defineProperties(this, { + $rawNode: { enumerable: false }, + $nodeID: { writable: false }, + _center: { enumerable: false }, + $center: { get: this.getCenter }, + $isUturn: { writable: false }, + $isEditable: { writable: false }, + $isPartial: { writable: false }, + _rawRestrictions: { enumerable: false }, + _rawRestrictionIDs: { enumerable: false }, + _restrictions: { enumerable: false }, + $restrictions: { get: this.getRestrictions }, + _rawOtherSegments: { enumerable: false }, + _otherSegments: { enumerable: false }, + $otherSegments: { get: this.getOtherSegments }, + _rawOutConnections: { enumerable: false }, + _outConnections: { enumerable: false }, + $outConnections: { get: this.getOutConnections }, + _rawInConnections: { enumerable: false }, + _inConnections: { enumerable: false }, + $inConnections: { get: this.getInConnections }, + $restrictionsLen: { writable: false }, + $otherSegmentsLen: { writable: false }, + $outConnectionsLen: { writable: false }, + $inConnectionsLen: { writable: false }, + }); + } + /** + * Get center + * @returns {OpenLayers.LonLat} + */ + SimpleNODE.prototype.getCenter = function () { + if (this._center) return this._center; + + if (!this.$rawNode) return null; + + var bounds = this.$rawNode.geometry.bounds; + this._center = new OpenLayers.LonLat(bounds.left, bounds.bottom) + .transform(WM.projection, WM.displayProjection); + // round the lon/lat + this._center.lon = Math.round(this._center.lon * 1e5) / 1e5; + this._center.lat = Math.round(this._center.lat * 1e5) / 1e5; + return this._center; + }; + /** + * Get restrictions + * *returns {Array.} + */ + SimpleNODE.prototype.getRestrictions = function () { + var t; + return this._restrictions ? this._restrictions : + (t = this, this._restrictions = this._rawRestrictions.map( + function (e, i) { + return new SimpleRESTRICTION(e, t._rawRestrictionIDs[i]) + }) + ); + } + /** + * Get outward connection + * *returns {SimpleSEGMENT} + */ + SimpleNODE.prototype.getOutConnections = function () { + return this._outConnections ? this._outConnections : + this._outConnections = this._rawOutConnections.map( + function (e) { return new SimpleSEGMENT(e) }); + } + /** + * Get inward connection + * *returns {SimpleSEGMENT} + */ + SimpleNODE.prototype.getInConnections = function () { + return this._inConnections ? this._inConnections : + this._inConnections = this._rawInConnections.map( + function (e) { return new SimpleSEGMENT(e) }); + } + /** + * Get another segment + * *returns {SimpleSEGMENT} + */ + SimpleNODE.prototype.getOtherSegments = function () { + return this._otherSegments ? this._otherSegments : + this._otherSegments = this._rawOtherSegments.map( + function (e) { return new SimpleSEGMENT(e) }); + } + + + /** + * Simple restriction object constructor + * @constructor + * @struct + * @param {Waze.RESTRICTION} obj + * @param {number} segID + */ + function SimpleRESTRICTION(obj, segID) { + // cached node + /** *type {SimpleSEGMENT} */ + this._to = null; + this.$to = null; + /** @type {number} */ + this.$toID = segID; + /** @type {boolean} */ + this.$allDay = obj.allDay || false; + /** @type {number} */ + this.$days = obj.days; + /** @type {string} */ + this.$description = (obj.description || ""); + /** @type {boolean} */ + this.$isInThePast = obj.isInThePast() || false; + /** @type {boolean} */ + this.$isEnabled = obj.enabled || false; + /** @type {string} */ + this.$fromDate = (obj.fromDate || ""); + /** @type {string} */ + this.$fromTime = (obj.fromTime || ""); + /** @type {string} */ + this.$toDate = (obj.toDate || ""); + /** @type {string} */ + this.$toTime = (obj.toTime || ""); + /** @type {number} */ + this.$vehicles = obj.vehicleTypes; + + Object.defineProperties(this, { + _to: { enumerable: false }, + $to: { get: this.getTo }, + $toID: { writable: false }, + $allDay: { writable: false }, + $days: { writable: false }, + $description: { writable: false }, + $isInThePast: { writable: false }, + $isEnabled: { writable: false }, + $fromDate: { writable: false }, + $fromTime: { writable: false }, + $toDate: { writable: false }, + $toTime: { writable: false }, + $vehicles: { writable: false } + }); + } + /** + * Get to segment + * *returns {SimpleSEGMENT} + */ + SimpleRESTRICTION.prototype.getTo = function () { + return this._to ? this._to : + this._to = new SimpleSEGMENT(this.$toID); + }; + + /** + * Simple representation of a segment constructor + * @constructor + * @struct + * @param {number} objID + */ + function SimpleSEGMENT(objID) { + // cached segment + /** @type {Waze.SEGMENT} */ + this.$rawSegment = null; + // cached node + /** *type {SimpleNODE} */ + this._nodeA = null; + this.$nodeA = null; + this.$nodeAID = 0; + // cached node + /** *type {SimpleNODE} */ + this._nodeB = null; + this.$nodeB = null; + this.$nodeBID = 0; + // cached center + /** @type {OpenLayers.LonLat} */ + this._center = null; + this.$center = null; + // cached restriction + /** @type {Array.} */ + this._ABRestrictions = null; + /** @type {Array.} */ + this.$ABRestrictions = null; + // cached restriction + /** @type {Array.} */ + this._BARestrictions = null; + /** @type {Array.} */ + this.$BARestrictions = null; + + /** @type {number} */ + this.$segmentID = objID; + /** *type {_WV.SimpleADDRESS} */ + this.$address = null; + /** @type {boolean} */ + this.$isTurnALocked = false; + /** @type {boolean} */ + this.$isTurnBLocked = false; + /** @type {boolean} */ + this.$isRoundabout = false; + /** @type {boolean} */ + this.$hasHNs = false; + /** @type {boolean} */ + this.$isEditable = false; + /** @type {boolean} */ + this.$forceNonEditable = false; + /** @type {number} */ + this.$type = 0; + /** @type {number} */ + this.$typeRank = 0; + /** @type {number} */ + this.$direction = 0; + /** @type {boolean} */ + this.$isToll = false; + /** @type {number} */ + this.$elevation = 0; + /** @type {number} */ + this.$lock = 0; + /** @type {number} */ + this.$rank = 0; + /** @type {number} */ + this.$length = 0; + /** @type {string} */ + this.$updatedOn = ""; + /** @type {string} */ + this.$updatedBy = ""; + /** @type {number} */ + this.$updatedByID = 0; + /** @type {number} */ + this.$updatedByLevel = 0; + /** @type {string} */ + this.$createdOn = ""; + /** @type {string} */ + this.$createdBy = ""; + /** @type {number} */ + this.$createdByID = 0; + /** @type {number} */ + this.$createdByLevel = 0; + /** @type {Array} */ + this.$alts = []; + /** @type {number} */ + this.$ABRestrictionsLen = 0; + /** @type {number} */ + this.$BARestrictionsLen = 0; + + var seg = WMo.segments.get(objID); + if (classCodeIs(seg, CC_UNDEFINED) || classCodeIs(seg, CC_NULL)) + return; + + var attrs = seg.attributes; + + this.$rawSegment = seg; + this.$nodeAID = attrs.fromNodeID; + this.$nodeBID = attrs.toNodeID; + + this.$address = new _WV.SimpleADDRESS(attrs.primaryStreetID); + + this.$isTurnALocked = attrs.revTurnsLocked; + this.$isTurnBLocked = attrs.fwdTurnsLocked; + this.$isRoundabout = classCodeDefined(attrs.junctionID) + && null !== attrs.junctionID; + this.$hasHNs = attrs.hasHNs; + this.$isEditable = seg.arePropertiesEditable(); + this.$type = attrs.roadType; + this.$typeRank = this.getTypeRank(attrs.roadType); + + this.$direction = seg.getDirection(); + this.$isToll = seg.isTollRoad(); + this.$elevation = attrs.level; + this.$lock = attrs.lockRank + 1; + this.$rank = attrs.rank + 1; + if ("length" in attrs) + this.$length = attrs.length; + else + this.$length = Math.round(seg.geometry.getGeodesicLength(WM.projection)); + + if (attrs.updatedOn) + this.$updatedOn = formatDate(attrs.updatedOn); + if (0 < attrs.updatedBy) { + this.$updatedByID = attrs.updatedBy; + this.$updatedBy = getUserName(attrs.updatedBy); + this.$updatedByLevel = getUserLevel(attrs.updatedBy); + } + if (attrs.createdOn) + this.$createdOn = formatDate(attrs.createdOn); + if (attrs.createdBy) { + this.$createdByID = attrs.createdBy; + this.$createdBy = getUserName(attrs.createdBy); + this.$createdByLevel = getUserLevel(attrs.createdBy); + } + this.$alts = attrs.streetIDs.map(function (objID) { + return new _WV.SimpleADDRESS(objID); + }); + this.$ABRestrictionsLen = attrs.fwdRestrictions ? + attrs.fwdRestrictions.length : 0; + this.$BARestrictionsLen = attrs.revRestrictions ? + attrs.revRestrictions.length : 0; + + // mark some properties as readonly + Object.defineProperties(this, { + $rawSegment: { enumerable: false }, + _nodeA: { enumerable: false }, + $nodeA: { get: this.getNodeA }, + $nodeAID: { writable: false }, + _nodeB: { enumerable: false }, + $nodeB: { get: this.getNodeB }, + $nodeBID: { writable: false }, + _center: { enumerable: false }, + $center: { get: this.getCenter }, + _ABRestrictions: { enumerable: false }, + $ABRestrictions: { get: this.getABRestrictions }, + _BARestrictions: { enumerable: false }, + $BARestrictions: { get: this.getRevRestrictions }, + $segmentID: { writable: false }, + $isTurnALocked: { writable: false }, + $isTurnBLocked: { writable: false }, + $isRoundabout: { writable: false }, + $hasHNs: { writable: false }, + $typeRank: { writable: false }, + $isEditable: { writable: false }, + $rank: { writable: false }, + $length: { writable: false }, + $updatedOn: { writable: false }, + $updatedBy: { writable: false }, + $updatedByID: { writable: false }, + $updatedByLevel: { writable: false }, + $createdOn: { writable: false }, + $createdBy: { writable: false }, + $createdByID: { writable: false }, + $createdByLevel: { writable: false }, + $ABRestrictionsLen: { writable: false }, + $BARestrictionsLen: { writable: false }, + }); + } + /** + * Get road type rank + */ + SimpleSEGMENT.prototype.getTypeRank = function (typeID) { + return { + // RT_RUNWAY = 19; + 19: 1, + // RT_RAILROAD = 18; + 18: 2, + // RT_STAIRWAY = 16; + 16: 3, + // RT_BOARDWALK = 10; + 10: 4, + // RT_TRAIL = 5; + 5: 5, + + // RT_PRIVATE = 17; + 17: 6, + // RT_PARKING = 20; + 20: 7, + // RT_DIRT = 8; + 8: 8, + + // RT_SERVICE = 21; + 21: 9, + // RT_STREET = 1; + 1: 10, + // RT_PRIMARY = 2; + 2: 11, + + // RT_RAMP = 4; + 4: 12, + // RT_MINOR = 7; + 7: 13, + // RT_MAJOR = 6; + 6: 14, + // RT_FREEWAY = 3; + 3: 15, + }[typeID]; + } + /** + * Get Node A + * *returns {SimpleNODE} + */ + SimpleSEGMENT.prototype.getNodeA = function () { + return this._nodeA ? this._nodeA : + this._nodeA = new SimpleNODE(this.$nodeAID, + this.$segmentID) + }; + /** + * Get Node B + * *returns {SimpleNODE} + */ + SimpleSEGMENT.prototype.getNodeB = function () { + return this._nodeB ? this._nodeB : + this._nodeB = new SimpleNODE(this.$nodeBID, + this.$segmentID) + }; + /** + * Get center + * @returns {OpenLayers.LonLat} + */ + SimpleSEGMENT.prototype.getCenter = function () { + if (this._center) return this._center; + + this._center = this.$rawSegment.geometry.bounds.getCenterLonLat().clone() + .transform(WM.projection, WM.displayProjection); + // round the lon/lat + this._center.lon = Math.round(this._center.lon * 1e5) / 1e5; + this._center.lat = Math.round(this._center.lat * 1e5) / 1e5; + return this._center; + }; + /** + * Get forward restrictions + * *returns {SimpleRESTRICTION} + */ + SimpleSEGMENT.prototype.getABRestrictions = function () { + var t; + return this._ABRestrictions ? this._ABRestrictions : + this._ABRestrictions = + (t = this, this.$rawSegment.attributes.fwdRestrictions.map( + function (e) { + return new SimpleRESTRICTION(e, t.$segmentID) + }) + ); + } + /** + * Get reverse restrictions + * *returns {SimpleRESTRICTION} + */ + SimpleSEGMENT.prototype.getRevRestrictions = function () { + var t; + return this._BARestrictions ? this._BARestrictions : + this._BARestrictions = + (t = this, this.$rawSegment.attributes.revRestrictions.map( + function (e) { + return new SimpleRESTRICTION(e, t.$segmentID) + }) + ); + } + /** + * Report a segment + * + * params is an object or just a check ID: + * params.$checkID - check ID + * params.$param - optional check param + * params.$cityParam - optional report city parameter + * params.$streetParam - optional report street parameter + * + * @param {Object|number} params + */ + SimpleSEGMENT.prototype.report = function (params) { + if (classCodeIs(params, CC_NUMBER)) + params = { $checkID: params }; + var id = params.$checkID; + if (!id + || !isLimitOk(id)) + return; + + /** + * Creates a copy of the segment for the report + * @param {SimpleSEGMENT} ss + */ + function getSegmentCopy(ss) { + /** @struct */ + return { + $segmentID: +ss.$segmentID, + $countryID: +ss.$address.$countryID, + $cityID: +ss.$address.$cityID, + $streetID: +ss.$address.$streetID, + $reportIDs: {}, + $updated: ss.$updatedOn ? ss.$rawSegment.attributes.updatedOn + : (ss.$createdOn ? ss.$rawSegment.attributes.createdOn : 0), + $userID: ss.$updatedByID ? +ss.$updatedByID + : (ss.$createdByID ? +ss.$createdByID : 0), + $isEditable: ss.$isEditable + && (ss.$nodeA.$isEditable || ss.$nodeA.$isPartial) + && (ss.$nodeB.$isEditable || ss.$nodeB.$isPartial) + , + $typeRank: +ss.$typeRank, + $center: ss.$center, + $length: +ss.$length, + }; + } + + // shortcuts + var rep = _REP.$cityIDs[this.$address.$cityID]; + var check = _RT.$checks[id]; + + // increase report counters + if (_repRC[id]) _repRC[id]++; + else _repRC[id] = 1; + if (LIMIT_PERCHECK < _repRC[id]) { + _REP.$isLimitPerCheck = true; + return; + } + + // city + if (params.$cityParam) + rep.$params[id] = params.$cityParam; + + // street + /** @const */ + var sid = this.$address.$streetID; + if (!(sid in rep.$streetIDs)) { + // new street + rep.$unsortedStreetIDs.push(sid) + _repS[sid] = this.$address.$street; + rep.$streetIDs[sid] = {}; + rep.$streetIDs[sid].$params = {}; + rep.$streetIDs[sid].$segmentIDs = {}; + rep.$streetIDs[sid].$unsortedSegmentIDs = []; + rep.$streetIDs[sid].$sortedSegmentIDs = []; + } + rep = rep.$streetIDs[sid]; + if (params.$streetParam) + rep.$params[id] = params.$streetParam; + + // segment + if (!(this.$segmentID in rep.$segmentIDs)) { + // new segment + rep.$unsortedSegmentIDs.push(this.$segmentID); + rep.$segmentIDs[this.$segmentID] = getSegmentCopy(this); + } + var segmentCopy = rep.$segmentIDs[this.$segmentID]; + + // add an user + var uid = segmentCopy.$userID; + if (!(uid in _repU)) { + var n = ""; + + if (uid === this.$createdByID) + n = this.$createdBy; + else if (uid === this.$updatedByID) + n = this.$updatedBy; + _repU[uid] = n; + } + + // force segment to be non-editable + var seenObj = _RT.$seen[this.$segmentID]; + if (this.$forceNonEditable) { + this.$forceNonEditable = false; + segmentCopy.$isEditable = false; + // force update max severity + if (_REP.$maxSeverity <= seenObj[I_SEVERITY] + || _REP.$maxSeverity <= check.SEVERITY) + bUpdateMaxSeverity = true; + } + + // mark segment as reported + segmentCopy.$reportIDs[id] = params.$param; + + // update max severity + if (_REP.$maxSeverity < check.SEVERITY) { + if (checkFilter(check.SEVERITY, segmentCopy, null) + && getFilteredSeverity(check.SEVERITY, id, true)) + _REP.$maxSeverity = check.SEVERITY; + } + + // mark raw segment as highlighted + if (!check.REPORTONLY && seenObj[I_SEVERITY] < check.SEVERITY) + seenObj[I_SEVERITY] = check.SEVERITY; + seenObj[I_SEGMENTCOPY] = segmentCopy; + }; + /** + * Increase city counter + */ + SimpleSEGMENT.prototype.incCityCounter = function () { + // shortcuts + var rep = _REP.$cityIDs; + /** @const */ + var cid = this.$address.$cityID; + + // city + if (!(cid in rep)) { + // new city + _REP.$countries[this.$address.$countryID] = this.$address.$country; + _REP.$unsortedCityIDs.push(cid); + + _repC[cid] = this.$address.$city; + _repCC[cid] = 0; + rep[cid] = {}; + rep[cid].$params = {}; + rep[cid].$streetIDs = {}; + rep[cid].$unsortedStreetIDs = []; + rep[cid].$sortedStreetIDs = []; + } + // increase city counter + _repCC[cid]++; + // increase total counter + _REP.$counterTotal++; + }; + /** + * Delete city check + * _REP->$cityIDs->streetIDs->$segmentIDs->$reportIDs + */ + function deleteCityCheck(cityID, checkID) { + var repS = _REP.$cityIDs[cityID].$streetIDs + for (var sid in repS) { + if (!repS.hasOwnProperty(sid)) continue; + + var repSG = repS[sid].$segmentIDs; + for (var sgid in repSG) { + if (!repSG.hasOwnProperty(sgid)) continue; + + /** @const */ + var reportIDs = repSG[sgid].$reportIDs; + if (!(checkID in reportIDs)) continue; + + // recalculate seen severity + /** @const */ + var seen = _RT.$seen[sgid]; + var maxSev = 0; + var filSev = 0; + + delete reportIDs[checkID]; + for (var repID in reportIDs) { + if (!reportIDs.hasOwnProperty(repID)) continue; + + var check = _RT.$checks[repID]; + if (!check) continue; + + if (filSev < check.SEVERITY + && getFilteredSeverity(check.SEVERITY, repID, true)) { + filSev = check.SEVERITY; + } + + if (maxSev < check.SEVERITY) { + maxSev = check.SEVERITY; + if (_RT.$curMaxSeverity === maxSev) + break; + } + } + seen[I_SEVERITY] = maxSev; + // rehighlight segment + reHLSegmentID(+sgid, filSev); + } // for all segments + } // for all streets + } + /** + * Returns city object for comparison + */ + function getCityCmpObj(cityID, city, otherCity) { + var obj = { + $cityID: cityID, + $counterReported: 0, + $limit: 0, + $city: city, + $otherCity: otherCity, + $CITY: city.toUpperCase(), + $noCountyCity: "", + $noAbbreviationCity: "", + $sortedCity: "", + $noSpaceCity: "", + $reason: "", + }; + obj.$noCountyCity = obj.$CITY + .replace(/ *\([^\)]+\) */g, "") + .replace(/ *\,.*/g, "") + ; + obj.$noAbbreviationCity = obj.$noCountyCity + .replace(/ *[^\. ]+ *\. */g, "") + ; + obj.$noDigitsCity = obj.$noCountyCity + .replace(/ *\d+ */g, " ") + ; + obj.$sortedCity = obj.$noAbbreviationCity + .split(" ").sort().join(" "); + obj.$noSpaceCity = obj.$noAbbreviationCity + .split(" ").join(""); + return obj; + } + /** + * Sets limit for city object + */ + function setCmpObjLimits(obj1, obj2) { + var curCase = ''; + // if obj1 is identical to obj2 + if (obj1.$city === obj2.$city) { + // same name, but different IDs + curCase = trS("city.12") + ' ' + obj1.$cityID + ' & ' + obj2.$cityID; + obj1.$reason = obj2.$reason = curCase; + + obj1.$limit = obj2.$limit = 100; + return; + } + // De Witt & Dewitt + if (obj1.$noSpaceCity !== obj1.$noAbbreviationCity // obj1 has a space + && obj1.$noSpaceCity === obj2.$noAbbreviationCity // DeWitt & Dewitt + ) { + obj1.$reason = trS("city.13r"); + obj2.$reason = trS("city.13a"); + + obj1.$limit = 10; + obj2.$limit = 1e3; + return; + } + // if obj1 is part of obj2 + if (new RegExp("(^| )" + obj1.$sortedCity).test(obj2.$sortedCity)) { + // if obj1 has an abbreviation + if (obj1.$noCountyCity.length !== obj1.$noAbbreviationCity.length) { + // Poreba Wlk. vs Poreba Wielka -> always report Poreba Wlk. + // Poreba Wlk. vs Wielka Poreba -> always report Poreba Wlk. + curCase = trS("city.2"); + obj1.$reason = obj2.$reason = curCase; + + obj1.$limit = 1e3; + // report up to 10 segments of Poreba Wielka + obj2.$limit = 10; + return; + } + // if obj1 is very short + if (3 > obj1.$city.length) { + // kr vs Krakow -> drop Krakow, always report kr + curCase = trS("city.3"); + obj1.$reason = obj2.$reason = curCase; + + obj1.$limit = 1e3; + obj2.$limit = 0; + return; + } + // if obj1 and obj2 are the same + if (obj1.$CITY === obj2.$CITY) { + // poreba vs Poreba -> report lowercase poremba + curCase = trS("city.5"); + obj1.$reason = obj2.$reason = curCase; + + if (obj1.$city.charAt(0) !== obj1.$CITY.charAt(0)) { + obj1.$limit = 1e3; + obj2.$limit = 10; + } + else { + obj1.$limit = 10; + obj2.$limit = 1e3; + } + return; + } + // if sorted obj1 and obj2 are the same + if (obj1.$sortedCity === obj2.$sortedCity) { + if (obj1.$noSpaceCity !== obj1.$noAbbreviationCity) { + // Wielka Poreba vs Poreba Wielka + curCase = trS("city.6"); + obj1.$reason = obj2.$reason = curCase; + + obj1.$limit = obj2.$limit = 1e3; + return; + } + if (obj1.$city.length === obj1.$noCountyCity.length) { + if (obj2.$city.length === obj2.$noCountyCity.length) { + // Poreba Wlk. vs Poreba Wiel. -> report both + curCase = trS("city.7"); + obj1.$reason = obj2.$reason = curCase; + + obj1.$limit = obj2.$limit = 1e3; + } + else { + // Vyshneve vs Vyshneve (smth) -> always report Vyshneve + // Vyshneve vs Vyshneve, smth -> always report Vyshneve + // curCase = 'City vs City (county)'; + obj1.$reason = trS("city.8a"); + obj2.$reason = trS("city.8r"); + + obj1.$limit = 1e3; + obj2.$limit = 10; + } + return; + } + if (obj2.$city.length === obj2.$noCountyCity.length) { + // Poreba Wlk. (smth) vs Poreba Wiel. -> report both + //curCase = 'City (county) vs City'; + obj1.$reason = trS("city.8r"); + obj2.$reason = trS("city.8a"); + + obj1.$limit = 10; + obj2.$limit = 1e3; + } + else { + // Vyshneve (smth1) vs Vyshneve (smth2) -> report both + curCase = trS("city.9"); + obj1.$reason = obj2.$reason = curCase; + + obj1.$limit = obj2.$limit = 1e3; + } + return; + } // sorted objects are the same + + // other partial matches + if (new RegExp(obj1.$sortedCity + "( |$)").test(obj2.$sortedCity)) { + if (4 < obj2.$sortedCity.length - obj1.$sortedCity.length) { + // Amsterdam vs Amsterdam Smth -> drop both + obj1.$reason = trS("city.10a"); + obj2.$reason = trS("city.10r"); + + obj1.$limit = obj2.$limit = 10; + return; + } + if (obj1.$noDigitsCity === obj2.$noDigitsCity) { + // Строитель снт (Волжский) = Строитель 2 снт (Среднеахтубинский р-н) + curCase = trS("city.14"); + obj1.$reason = obj2.$reason = curCase; + + obj1.$limit = obj2.$limit = 1; + return; + } + // Renens vs Renens VD -> report Renens + curCase = trS("city.11"); + obj1.$reason = obj2.$reason = curCase; + + obj1.$limit = 1e3; + obj2.$limit = 10; + return; + } + // krak vs Krakow -> drop Krakow, report krak + curCase = trS("city.4"); + obj1.$reason = obj2.$reason = curCase; + + obj1.$limit = 1e3; + obj2.$limit = 10; + return; + } // obj1 is a part of obj2 + } // setCmpObjLimits + + /** + * Add HLed Segments to the Layer + */ + function addHLedSegments() { + if (RTStateIs(ST_RUN) || RTStateIs(ST_CONTINUE)) + return; + + var features = []; + for (var i in _RT.$HLedObjects) { + if (!_RT.$HLedObjects.hasOwnProperty(i)) continue; + var obj = _RT.$HLedObjects[i]; + + if (obj.$severity) + features.push(new OpenLayers.Feature.Vector( + obj.$geometry.clone(), { 0: obj.$severity } + )); + } + _RT.$HLlayer.destroyFeatures(); + _RT.$HLlayer.addFeatures(features); + } + + /** + * Highlight reported segments + */ + function HLSegment(rawSegment) { + if (RTStateIs(ST_RUN) || RTStateIs(ST_CONTINUE)) + return; + + var segmentID = rawSegment.getID(); + var seenObj = _RT.$seen[segmentID]; + var severity = seenObj[I_SEVERITY]; + var segmentCopy = seenObj[I_SEGMENTCOPY]; + + // check filter + if (!severity || !segmentCopy + || !checkFilter(severity, segmentCopy, null)) + return; + + var filteredSeverity = getFilteredSeverityObj(severity, + segmentCopy.$reportIDs, true); + if (!filteredSeverity) + return; + + // add HL object + /** @struct */ + var obj = { + $severity: filteredSeverity, + $geometry: rawSegment.geometry, + }; + _RT.$HLedObjects[segmentID] = obj; + } + /** + * Rehighlight reported segments + */ + function reHLSegmentID(segmentID, newSeverity) { + if (RTStateIs(ST_RUN) || RTStateIs(ST_CONTINUE)) + return; + + // force update max severity + if (_REP.$maxSeverity !== newSeverity) + bUpdateMaxSeverity = true; + + // check for exclude notes option + if (oExcludeNotes && RS_NOTE === newSeverity) + newSeverity = 0; + + if (segmentID in _RT.$HLedObjects) { + var hlObj = _RT.$HLedObjects[segmentID]; + hlObj.$severity = newSeverity; + } + } + /** + * Delete seen segment + * _REP->$cityIDs->streetIDs->$segmentIDs->$reportIDs + */ + function deleteSeenSegment(segmentID) { + // set no severity + reHLSegmentID(segmentID, 0); + var seen = null; + if (segmentID in _RT.$seen) + seen = _RT.$seen[segmentID]; + if (!seen) + return; + + // force update max severity + // use <= because it might be the last segment + if (_REP.$maxSeverity <= seen[I_SEVERITY]) + bUpdateMaxSeverity = true; + + var segmentCopy = seen[I_SEGMENTCOPY]; + var cityID = seen[I_CITYID]; + delete _RT.$seen[segmentID]; + + // uncount total counter + if (0 < _REP.$counterTotal) + _REP.$counterTotal--; + // uncount city counter + if (0 < _repCC[cityID]) + _repCC[cityID]--; + if (!segmentCopy) + return; + + var repC = _REP.$cityIDs; + for (var cid in repC) { + if (!repC.hasOwnProperty(cid)) continue; + + var repS = repC[cid].$streetIDs + for (var sid in repS) { + if (!repS.hasOwnProperty(sid)) continue; + + var repSG = repS[sid].$segmentIDs; + for (var sgid in repSG) { + if (!repSG.hasOwnProperty(sgid) + || +sgid !== segmentID) + continue; + + /** @const */ + var reportIDs = repSG[sgid].$reportIDs; + + // uncount report counters + /** @const */ + for (var repID in reportIDs) { + if (!reportIDs.hasOwnProperty(repID)) continue; + + if (0 < _repRC[repID]) + _repRC[repID]--; + } + + // delete reported segment + delete repSG[sgid]; + + // delete the segment from unsorted array + var repUSG = repS[sid].$unsortedSegmentIDs; + repUSG.splice(repUSG.indexOf(+sgid), 1); + + // clear sorted array + repS[sid].$sortedSegmentIDs = []; + + return; + } // for all segments + } // for all streets + } // for all cities + } + + /** + * Update segment properties + */ + function updateSegmentProperties(selectedSegments, disabledHL) { + if (RTStateIs(ST_RUN) || RTStateIs(ST_CONTINUE)) + return; + + // remove WV properties + var prop = document.getElementById("i" + ID_PROPERTY) + var propDis = document.getElementById("i" + ID_PROPERTY_DISABLED) + + var defID = ID_PROPERTY; + var defHTML = ''; + if (disabledHL) { + defID = ID_PROPERTY_DISABLED; + defHTML = '
    ' + + ' ' + + trS("props.disabled") + + '
    ' + ; + // remove prop + if (prop) { + prop.parentNode.removeChild(prop); + } + prop = propDis; + } + else { + // remove propDis + if (propDis) { + propDis.parentNode.removeChild(propDis); + } + } + + if (prop) + prop.innerHTML = defHTML; + else { + var segmentProperties = document.getElementsByClassName("segment")[0]; + if (segmentProperties) { + var refElement = null; + for (var i = 0; i < segmentProperties.children.length; i++) { + var c = segmentProperties.children[i]; + if ("selection-text" === c.className) + continue; + + refElement = c; + break; + } + if (refElement) { + var d = document.createElement("div"); + d.innerHTML = defHTML; + d.id = "i" + defID; + prop = segmentProperties.insertBefore(d, refElement); + } + } // if segmentProperties + } // if prop + + if (disabledHL) + return; + + // check if there are any segment selected + if (!selectedSegments.length) + return; + + // find selected issues + var selectedIssues = []; + for (var i = 0; i < selectedSegments.length; i++) { + var segmentID = selectedSegments[i]; + if (segmentID in _RT.$seen) { + var segmentCopy = _RT.$seen[segmentID][I_SEGMENTCOPY]; + if (!segmentCopy) continue; + // segment is selected and highlighted + for (var cid in segmentCopy.$reportIDs) { + if (segmentCopy.$reportIDs.hasOwnProperty(cid)) { + var check = _RT.$checks[cid]; + if (check.REPORTONLY) + continue; + + selectedIssues.push([check, segmentCopy, cid]); + } + } + } + } // for all selected segments + + var newProp = '' + WV_NAME + + ' ' + trS("props.reports") + ':' + ; + if (_REP.$isLimitPerCheck) { + newProp += '
    ' + + '' + + ' ' + + trS("props.limit.title") + + '' + + '
    ' + + '' + + '
    ' + + trS("props.limit.problem") + + '.
    ' + + '' + + '
    ' + + '

    ' + trS("props.limit.solution") + '.

    ' + + '

    ' + ; + } // limit per check + + // exceptions note + if (skippedSegment) { + newProp += '
    ' + + '' + + ' ' + + trS("props.skipped.title") + + '' + + '
    ' + + '' + + '
    ' + + trS("props.skipped.problem") + + '.
    ' + + '

    ' + ; + } + + + if (!selectedIssues.length) { + // update properties + if (prop && (_REP.$isLimitPerCheck || skippedSegment)) + prop.innerHTML = newProp; + return; + } + + // sort the issues + selectedIssues.sort(function (a, b) { return cmpCheckIDs(a[2], b[2]) }); + + // only unique issues + var selectedCounters = {}; + selectedIssues = selectedIssues.filter(function (e, i, arr) { + var checkID = e[2]; + // skip first element + if (i && arr[i - 1][2] === checkID) { + selectedCounters[checkID]++; + return false; + } + selectedCounters[checkID] = 1; + return true; + }); + // create a list of issues + selectedIssues.forEach(function (e) { + var check = e[0]; + var segmentCopy = e[1]; + var checkID = e[2]; + var checkCounter = selectedCounters[checkID]; + var sevClass = 0; + var sevIcon = ""; + var sevBG = ""; + var strCountry = _REP.$countries[segmentCopy.$countryID]; + var ccode = ""; + + if (strCountry) + ccode = _I18n.getCountryCode(strCountry.toUpperCase()); + else { + // try top country + ccode = _RT.$cachedTopCCode; + } + options = trO(check.OPTIONS, ccode); + + switch (check.SEVERITY) { + case RS_NOTE: + sevClass = CL_NOTE; + sevIcon = "info-circle"; + sevBG = GL_NOTEBGCOLOR; + break; + case RS_WARNING: + sevClass = CL_WARNING; + sevIcon = "exclamation-triangle"; + sevBG = GL_WARNINGBGCOLOR; + break; + case RS_ERROR: + sevClass = CL_ERROR; + sevIcon = "times-circle"; + sevBG = GL_ERRORBGCOLOR; + break; + case RS_CUSTOM1: + sevClass = CL_CUSTOM1; + sevIcon = "user"; + sevBG = GL_CUSTOM1BGCOLOR; + break; + case RS_CUSTOM2: + sevClass = CL_CUSTOM2; + sevIcon = "user"; + sevBG = GL_CUSTOM2BGCOLOR; + break; + } + var shortTitle = exSOS(check.TITLE, options, "titleEN") + .replace("WME Color Highlights", "WMECH") + .replace("WME Toolbox", "WMETB"); + newProp += '
    ' + + '' + + ' ' + + shortTitle + + (1 < checkCounter ? ' (' + checkCounter + ')' : '') + + '' + + '
    ' + + '' + + '
    ' + + '#' + checkID + ' ' + + exSOS(check.PROBLEM, options, "problemEN") + ; + var pl = trO(check.PROBLEMLINK, ccode); + if (pl) { + newProp += ': ' + + trO(check.PROBLEMLINKTEXT, ccode) + + '' + ; + } + else + newProp += '.'; + + newProp += '
    '; + + // show howto + if (segmentCopy.$isEditable) { + newProp += '' + + '
    ' + ; + if (check.SOLUTION) { + newProp += '

    ' + exSOS(check.SOLUTION, options, "solutionEN"); + + var sl = trO(check.SOLUTIONLINK, ccode); + if (sl) { + newProp += ': ' + + trO(check.SOLUTIONLINKTEXT, ccode) + + '' + ; + } + else + newProp += '.'; + + newProp += '

    '; + } + } + else { + newProp += '' + + '
    ' + + '

    ' + trS("props.noneditable") + '.

    '; + ; + } + + // show params + var cityID = segmentCopy.$cityID; + var cityParam = _REP.$cityIDs[cityID].$params[checkID]; + if (cityParam) + newProp += '

    ' + cityParam + '

    '; + var streetID = segmentCopy.$streetID; + var streetParam = _REP.$cityIDs[cityID] + .$streetIDs[streetID].$params[checkID]; + + if (streetParam) + newProp += '

    ' + streetParam + '

    '; + + newProp += '

    ' + ; + }); // forEach + + // update properties + if (prop) + prop.innerHTML = newProp; + } // updateSegmentProperties + + /** + * Match regular expression + */ + function matchRegExp(checkID, segmentID, expandedString, options) { + var optRegExp = options[CO_REGEXP]; + if (!optRegExp) return false; + var optString = options[CO_STRING]; + var optBool = options[CO_BOOL]; + // debug + if (options[CO_NUMBER] + && 0 < _REP.$debugCounter) { + var checkTitle = ''; + if (_RT.$checks[checkID] && _RT.$checks[checkID].TITLE) + checkTitle = _RT.$checks[checkID].TITLE; + var reported = (optRegExp.test(expandedString) ? + (optBool ? false : true) + : (optBool ? true : false)); + _REP.$debugCounter--; + async(alog(getMsg("debug log for segment " + segmentID + ", check #" + checkID, + '\n1. ' + + (optString ? + 'Expand template: ' + optString + ' -> ' + : 'String: ') + + expandedString + + '\n2. Match RegExp: ' + + (optBool ? 'not ' : '') + + optRegExp + + ' -> ' + JSON.stringify(expandedString.match(optRegExp)) + + '\n=> ' + + (reported ? "REPORT the segment as #" + checkID + " '" + checkTitle + "'" + : "skip the segment") + + (0 < _REP.$debugCounter ? '' + : "\nEnd of debug log. Click '\u2718' (Clear report) button to start debug over.") + ))); + } + if (optRegExp.test(expandedString)) { + if (!optBool) + return true; + } + else { + if (optBool) + return true; + } + return false; // not match + } // matchRegExp + + + /////////////////////////////////////////////////////////////////////// + // FOR ALL SEGMENTS + + // shortcuts + // const: + var reportWMECH = _UI.pSettings.pScanner.oReportExt.CHECKED + && _RT.oReportWMECH.CHECKED; + var reportToolbox = _UI.pSettings.pScanner.oReportExt.CHECKED + && _RT.oReportToolbox.CHECKED; + var currentZoom = WM.getZoom(); + var slowChecks = _UI.pSettings.pScanner.oSlowChecks.CHECKED + && 3 < currentZoom; + var oExcludeNotes = _UI.pMain.pFilter.oExcludeNotes.CHECKED; + + var selectedSegments = []; + _RT.$HLedObjects = {}; + for (var segmentKey in WMo.segments.objects) { + var rawSegment = WMo.segments.objects[segmentKey]; + var segmentID = rawSegment.getID(); + + // SPECIAL CASE - skip all checks for locked segments (exceptions) + // 2014-05-01 + if (_RT.$topUser.$userLevel <= rawSegment.attributes.lockRank + && rawSegment.attributes.updatedOn + && 1398902400000 < rawSegment.attributes.updatedOn) { + if (rawSegment.selected) { + skippedSegment = true; + // add selected segment to the array + if (!DEF_DEBUG) + selectedSegments.push(segmentID); + } + if (!DEF_DEBUG) + continue; + } + + // skip unrendered features + if (rawSegment.layer + && rawSegment.id in rawSegment.layer.unrenderedFeatures) + continue; + + if ("Delete" === rawSegment.state) continue; + + var seen = null; + // check if the segment was already seen + if (segmentID in _RT.$seen) + seen = _RT.$seen[segmentID]; + + // always re-check selected segments + if (rawSegment.selected) { + // add selected segment to the array + selectedSegments.push(segmentID); + + // mark segment to revalidate + _RT.$revalidate[segmentID] = true; + // recheck selected segment if it's not highlighted by WMECH + if (seen && !seen[I_ISWMECHCOLOR]) { + deleteSeenSegment(segmentID); + seen = null; + } + } + else { + // recheck the segment to revalidate + if (segmentID in _RT.$revalidate) { + deleteSeenSegment(segmentID); + seen = null; + // unmark segment + delete _RT.$revalidate[segmentID]; + // delete WMECH color tag from the segment + delete rawSegment[GL_WMECHCOLOR]; + } + } + + // emulate WMECH_color + var segmentGeometry = document.getElementById(rawSegment.geometry.id); + if (!segmentGeometry) continue; + + var strokeColor = segmentGeometry.getAttribute("stroke").toUpperCase(); + if (4 === strokeColor.length) + strokeColor = '#' + strokeColor.charAt(1) + strokeColor.charAt(1) + + strokeColor.charAt(2) + strokeColor.charAt(2) + + strokeColor.charAt(3) + strokeColor.charAt(3); + if (strokeColor in _RT.$WMECHcolors) + rawSegment[GL_WMECHCOLOR] = strokeColor; + + // check if the segment was already seen + if (seen) { + // check if segment was highlighted before + var isTBColor = GL_TBCOLOR in rawSegment; + var isWMECHColor = GL_WMECHCOLOR in rawSegment; + + // if the segment was not partial and highlight is not changed + if (!seen[I_ISPARTIAL] + && (isTBColor === seen[I_ISTBCOLOR]) + && (isWMECHColor === seen[I_ISWMECHCOLOR]) + ) { + HLSegment(rawSegment); + continue; + } + } + + // check if start extent contains segment center + /* + if(RTStateIs(ST_RUN) + && !_RT.$startExtent.containsBounds(rawSegment.bounds, true, true)) + continue; + */ + + /////////////////////////////////////////////////////////////////// + // Prepare simple objects + var segment = new SimpleSEGMENT(segmentID); + Object.seal(segment); + + // shortcuts + var address = segment.$address; + var country = address.$country; + var countryLen = country.length; + var countryCode = country ? _I18n.getCountryCode(country.toUpperCase()) + : _RT.$cachedTopCCode; + var city = address.$city; + var cityLen = city.length; + var cityID = address.$cityID; + var street = address.$street; + var state = address.$state; + var streetLen = street.length; + var alts = segment.$alts; + + var roadType = segment.$type; + var typeRank = segment.$typeRank; + var isToll = segment.$isToll; + var direction = segment.$direction; + var elevation = segment.$elevation; + var lock = Math.max(segment.$lock, segment.$rank); + + var segmentLen = segment.$length; + + var isRoundabout = segment.$isRoundabout; + var hasHNs = segment.$hasHNs; + var isDrivable = RR_TRAIL < typeRank; + + var nodeA = segment.$nodeA; + var nodeB = segment.$nodeB; + var nodeAID = segment.$nodeAID; + var nodeBID = segment.$nodeBID; + var isPartial = nodeA.$isPartial || nodeB.$isPartial; + + var now = Date.now(); + + // check partial segment + if (seen) { + // if the segment is still partial + if (seen[I_ISPARTIAL] && isPartial) { + HLSegment(rawSegment); + continue; + } + // otherwise remove, recheck the segment and reanimate + deleteSeenSegment(segmentID); + seen = null; + } + + // mark segment as seen + _RT.$seen[segmentID] = seen = [0, null, + GL_TBCOLOR in rawSegment, GL_WMECHCOLOR in rawSegment, + isPartial || 4 > currentZoom, + cityID]; + + // increase city counter + segment.incCityCounter(); + + // check if any editable found + if (segment.$isEditable) + _REP.$isEditableFound = true; + + // check expiration date + if (0 === segmentID % 13 && segment.$updatedOn) { + var relDate = new Date(WV_RELEASE_VALID); + var nowDate = new Date(segment.$updatedOn); + if (0 > relDate.getTime() - nowDate) { + // destroy UI + _UI = {}; + error("This build of " + WV_NAME + " has expired. Please upgrade!"); + return; + } + } + + /////////////////////////////////////////////////////////////////// + // Checks + + /////////////////////////////////////////////////////////////////// + // SPECIAL CASES + + + // SPECIAL CASE - skip all the checks if there are no street ID + if (GL_NOID === street) { + deleteSeenSegment(segmentID); + continue; + } + + // check global access for the segment address + _RT.$isGlobalAccess = true; + if (!address.isOkFor(0)) { + _RT.$isGlobalAccess = false; + // get next segment + continue; + } + + // SPECIAL CASE - report overlapping segments even for new roads + if (slowChecks + && RT_RAILROAD !== roadType) { + if (nodeA.$otherSegmentsLen + && isLimitOk(118)) { + var rawNode = nodeA.$rawNode; + var baseAngle = rawNode.getAngleToSegment(rawSegment); + for (var i = 0; i < nodeA.$otherSegmentsLen; i++) { + var otherSegment = nodeA.$otherSegments[i]; + if (!otherSegment.$rawSegment) continue; + + var curAngle = rawNode.getAngleToSegment(otherSegment.$rawSegment); + var angle = Math.abs(baseAngle - curAngle); + if (angle > 180) angle = 360 - angle; + + if (2 > angle + && address.isOkFor(118)) { + segment.report(118); + break; + } + } + } // #118 + + if (nodeB.$otherSegmentsLen + && isLimitOk(119)) { + var rawNode = nodeB.$rawNode; + var baseAngle = rawNode.getAngleToSegment(rawSegment); + for (var i = 0; i < nodeB.$otherSegmentsLen; i++) { + var otherSegment = nodeB.$otherSegments[i]; + if (!otherSegment.$rawSegment) continue; + + var curAngle = rawNode.getAngleToSegment(otherSegment.$rawSegment); + var angle = Math.abs(baseAngle - curAngle); + if (angle > 180) angle = 360 - angle; + + if (2 > angle + && address.isOkFor(119)) { + segment.report(119); + break; + } + } + } // #119 + } // overlapping segments + + // SPECIAL CASE - skip all checks for new roads + if (!countryLen + && isLimitOk(23)) { + // mark new segment as not partial + seen[I_ISPARTIAL] = false; + + if (address.isOkFor(23)) { + // report and highlight the segment + segment.report(23); + HLSegment(rawSegment); + } + continue; + } + + // SPECIAL CASE - skip all checks for construction zones + if (streetLen + && address.isOkFor(101)) { + options = getCheckOptions(101, countryCode); + if (options[CO_REGEXP].test(street)) { + segment.report(101); + HLSegment(rawSegment); + continue; + } + } + + /////////////////////////////////////////////////////////////////// + // NO GROUP + + if (!state + && address.isOkFor(106)) + segment.report(106); + + + if (reportToolbox && address.isOkFor(CK_TBFIRST)) { + var col = rawSegment[GL_TBCOLOR]; + if (col) { + col = col.toUpperCase(); + for (var i = CK_TBFIRST; i <= CK_TBLAST; i++) { + var check = _RT.$checks[i]; + if (check.COLOR === col) { + segment.report(i); + break; + } + } + } + } + + if (reportWMECH && address.isOkFor(CK_WMECHFIRST)) { + var col = rawSegment[GL_WMECHCOLOR]; + if (col) { + for (var i = CK_WMECHFIRST; i <= CK_WMECHLAST; i++) { + var check = _RT.$checks[i]; + if (check && check.COLOR === col) { + segment.report(i); + break; + } + } + } + } + + if (alts.length + && address.isOkFor(34)) { + for (var i = 0; i < alts.length; i++) { + if (!alts[i].$street) { + segment.report(34); + break; + } + } + } + + if (slowChecks + && (segment.$ABRestrictionsLen || segment.$BARestrictionsLen) + && isLimitOk(38) + && address.isOkFor(38)) { + var restrictions = segment.$ABRestrictions.concat(segment.$BARestrictions); + for (var i = 0; i < restrictions.length; i++) { + if (restrictions[i].$isInThePast) { + segment.report(38); + break; + } + } + } + + if (slowChecks + && (nodeA.$restrictionsLen || nodeB.$restrictionsLen) + && isLimitOk(39) + && address.isOkFor(39)) { + var restrictions = nodeA.$restrictions.concat(nodeB.$restrictions); + for (var i = 0; i < restrictions.length; i++) { + var restriction = restrictions[i]; + if (restriction.$isInThePast) { + var param = ''; + if (restriction.$to.$address + && restriction.$to.$address.$street) + param = 'turn to ' + restriction.$to.$address.$street; + segment.report({ + $checkID: 39, + $streetParam: param, + }); + break; + } + } + } + + if (nodeAID + && nodeAID === nodeBID + && address.isOkFor(43)) + segment.report(43); + + if (RT_RAILROAD === roadType + && 100 > segmentLen + && !isPartial + && !nodeA.$otherSegmentsLen + && !nodeB.$otherSegmentsLen + && address.isOkFor(104)) + segment.report(104); + + if (RT_TRAIL === roadType + && -5 === elevation + && address.isOkFor(105)) + segment.report(105); + + if ((/*classCodeIs(elevation, CC_NULL) + ||*/ 9 < elevation + || -5 > elevation) + && address.isOkFor(116)) + segment.report(116); + + // custom checks + var expandOptions = { + "country": country, + "state": state, + "city": city, + "street": street, + // "altStreets": alts.length, + "altStreet": alts.map(function (e) { return e.$street }), + "altCity": alts.map(function (e) { return e.$city }), + + "type": roadType, + "typeRank": typeRank, + "toll": +isToll, + "direction": direction, + "elevation": elevation, + "lock": lock, + "length": segmentLen, + "ID": segmentID, + "roundabout": +isRoundabout, + "hasHNs": +hasHNs, + "drivable": +isDrivable, + + "Uturn": +(nodeA.$isUturn || nodeB.$isUturn), + "deadEnd": +!(isPartial || nodeA.$otherSegmentsLen && nodeB.$otherSegmentsLen), + + "partialA": +nodeA.$isPartial, + "deadEndA": +!(nodeA.$isPartial || nodeA.$otherSegmentsLen), + "segmentsA": nodeA.$otherSegmentsLen, + "inA": nodeA.$inConnectionsLen, + "outA": nodeA.$outConnectionsLen, + "UturnA": +nodeA.$isUturn, + + "partialB": +nodeB.$isPartial, + "deadEndB": +!(nodeB.$isPartial || nodeB.$otherSegmentsLen), + "segmentsB": nodeB.$otherSegmentsLen, + "inB": nodeB.$inConnectionsLen, + "outB": nodeB.$outConnectionsLen, + "UturnB": +nodeB.$isUturn, + "softTurns": +(!(segment.$isTurnALocked && segment.$isTurnBLocked)), + }; + for (var i = CK_MATCHFIRST; i <= CK_MATCHLAST; i++) { + if (!isLimitOk(i) + || !address.isOkFor(i)) + continue; + + options = getCheckOptions(i, countryCode); + var optString = options[CO_STRING]; + var optRegExp = options[CO_REGEXP]; + if (!optString || !optRegExp) continue; + + var expandedString = _I18n.expandSO(optString, expandOptions); + + if (matchRegExp(i, segmentID, expandedString, options)) + segment.report(i); + } // for all general match checks + + // mirror checks + /* + for(var i = CK_MIRRORFIRST; i <= CK_MIRRORLAST; i++) + { + if(!address.isOkFor(i)) continue; + + var nodes = {}; + nodes[i] = nodeA; + nodes[i + 100] = nodeB; + for(var checkID in nodes) + { + if(!isLimitOk(checkID)) continue; + + var node = nodes[checkID]; + window.console.log(checkID, node); + } + } // for all mirror checks + */ + + if (!cityLen + && RT_FREEWAY === roadType + && isLimitOk(69) + && address.isOkFor(69)) + segment.report(69); + + // NO GROUP END + + /////////////////////////////////////////////////////////////////// + // GROUP slowChecks + if (slowChecks) { + // GROUP slowChecks + // v 2 + if (1 === nodeA.$otherSegmentsLen + && DIR_UNKNOWN !== direction + && !nodeA.$isPartial + && !nodeA.$isUturn + // no turn restrictions + && !nodeA.$restrictionsLen + && !segment.$ABRestrictionsLen + && !segment.$BARestrictionsLen + && isLimitOk(36) + && address.isOkFor(36)) { + var otherSegment = nodeA.$otherSegments[0]; + var otherNode, nextNode; + if (otherSegment.$nodeAID === nodeAID) { + otherNode = otherSegment.$nodeA; + nextNode = otherSegment.$nodeB; + } + else { + otherNode = otherSegment.$nodeB; + nextNode = otherSegment.$nodeA; + } + if ((!nodeB.$isPartial || !nextNode.$isPartial) + && otherSegment.$segmentID !== segmentID + && otherSegment.$rawSegment + && (1e4 > (otherSegment.$length + segmentLen) || 1e3 > segmentLen) + && otherSegment.$address.$street === street + && otherSegment.$address.$city === city + && otherSegment.$address.$state === state + && otherSegment.$address.$country === country + && otherSegment.$type === roadType + && otherSegment.$isToll === isToll + // 2 & 2 || !2 && !2 + && ( + DIR_TWO === otherSegment.$direction && DIR_TWO === direction + || DIR_TWO !== otherSegment.$direction && DIR_TWO !== direction + ) + && otherSegment.$elevation === elevation + // simple loops + && otherSegment.$nodeAID !== nodeBID + && otherSegment.$nodeBID !== nodeBID + // restrictions + && !otherSegment.$ABRestrictionsLen + && !otherSegment.$BARestrictionsLen + && !otherNode.$restrictionsLen + && deepCompare(otherSegment.$alts, alts) + ) { + // check deep for loop + var loopFound = false; + for (var i = 0; i < nextNode.$otherSegmentsLen; i++) { + var thirdSegment = nextNode.$otherSegments[i]; + if (thirdSegment.$nodeAID === nodeBID + || thirdSegment.$nodeBID === nodeBID + ) { + loopFound = true; + break; + } + } + if (!loopFound) + segment.report(36); + } + } // unneeded node A + + // GROUP slowChecks + // v 2 + if (DIR_UNKNOWN !== direction + && !nodeB.$isPartial + && 1 === nodeB.$otherSegmentsLen + && !nodeB.$isUturn + // no turn restrictions + && !nodeB.$restrictionsLen + && !segment.$ABRestrictionsLen + && !segment.$BARestrictionsLen + && isLimitOk(37) + && address.isOkFor(37)) { + var otherSegment = nodeB.$otherSegments[0]; + var otherNode, nextNode; + if (otherSegment.$nodeAID === nodeBID) { + otherNode = otherSegment.$nodeA; + nextNode = otherSegment.$nodeB; + } + else { + otherNode = otherSegment.$nodeB; + nextNode = otherSegment.$nodeA; + } + if ((!nodeA.$isPartial || !nextNode.$isPartial) + && otherSegment.$segmentID !== segmentID + && otherSegment.$rawSegment + && 1e4 > (otherSegment.$length + segmentLen) + && otherSegment.$address.$street === street + && otherSegment.$address.$city === city + && otherSegment.$address.$state === state + && otherSegment.$address.$country === country + && otherSegment.$type === roadType + && otherSegment.$isToll === isToll + // 2 & 2 || !2 && !2 + && ( + DIR_TWO === otherSegment.$direction && DIR_TWO === direction + || DIR_TWO !== otherSegment.$direction && DIR_TWO !== direction + ) + && otherSegment.$elevation === elevation + // simple loops + && otherSegment.$nodeAID !== nodeAID + && otherSegment.$nodeBID !== nodeAID + // restrictions + && !otherSegment.$ABRestrictionsLen + && !otherSegment.$BARestrictionsLen + && !otherNode.$restrictionsLen + && deepCompare(otherSegment.$alts, alts) + ) { + // check deep for loop + var loopFound = false; + for (var i = 0; i < nextNode.$otherSegmentsLen; i++) { + var thirdSegment = nextNode.$otherSegments[i]; + if (thirdSegment.$nodeAID === nodeAID + || thirdSegment.$nodeBID === nodeAID + ) { + loopFound = true; + break; + } + } + if (!loopFound) + segment.report(37); + } + } // unneeded node B + + } // GROUP slowChecks + + + /////////////////////////////////////////////////////////////////// + // GROUP cityLen + if (cityLen) { + // GROUP cutyLen + // RegExp city name checks + for (var i = CK_CITYNAMEFIRST; i <= CK_CITYNAMELAST; i++) { + if (!address.isOkFor(i) || !isLimitOk(i)) + continue; + + if (matchRegExp(i, segmentID, city, + getCheckOptions(i, countryCode))) + segment.report(i); + } // for city name checks + + // v2.0 + // GROUP cityLen + if (isLimitOk(24) + && address.isOkFor(24)) { + var param = trS("city.1"); + var r = 3 > cityLen ? true : false; + + var cityCounter = _repCC[cityID]; + + // check new city + if (1 === cityCounter + || ((cityID in _REP.$incompleteIDs) + && !_REP.$incompleteIDs[cityID].$counterReported)) { + for (var i = 0, len = _REP.$unsortedCityIDs.length; i < len; i++) { + var cid = _REP.$unsortedCityIDs[i]; + if (cid === cityID) continue; + + var c = _repC[cid]; + var cLen = c.length; + if (1 > cLen) continue; + + var cityObj = getCityCmpObj(cityID, city, c); + var cObj = getCityCmpObj(cid, c, city); + setCmpObjLimits(cityObj, cObj); + setCmpObjLimits(cObj, cityObj); + + if (cityObj.$limit) + _REP.$incompleteIDs[cityID] = cityObj; + if (cObj.$limit && !_REP.$incompleteIDs[cid]) + _REP.$incompleteIDs[cid] = cObj; + if (cityObj.$limit || cObj.$limit) break; + } // for + } // new city + + // check if city was reported before + if (cityID in _REP.$incompleteIDs) { + // shortcut + var incompleteCity = _REP.$incompleteIDs[cityID]; + + // increase the counter + incompleteCity.$counterReported++; + + // check total limit + if (incompleteCity.$limit < cityCounter) { + r = false; + deleteCityCheck(cityID, 24); + delete _REP.$incompleteIDs[cityID]; + } + else { + r = true; + param = trS("city.consider") + + " " + incompleteCity.$otherCity + + " [" + incompleteCity.$reason + "]"; + } + } + if (r) { + segment.report({ + $checkID: 24, + $cityParam: param, + }); + } + } // CITYINCOMPLETE + + // GROUP cityLen + if (RT_RAILROAD === roadType + && isLimitOk(24) + && address.isOkFor(27)) + segment.report(27); + + // GROUP cityLen + if (RT_FREEWAY === roadType + && isLimitOk(59) + && address.isOkFor(59)) + segment.report(59); + + } // GROUP cityLen + + /////////////////////////////////////////////////////////////////// + // GROUP isDrivable + if (isDrivable) { + // GROUP isDrivable + if (slowChecks) { + if (nodeA.$outConnectionsLen + // exclude revCons + && (DIR_TWO === direction || DIR_BA === direction) + && isLimitOk(120)) { + var rawNode = nodeA.$rawNode; + var baseAngle = rawNode.getAngleToSegment(rawSegment); + for (var i = 0; i < nodeA.$outConnectionsLen; i++) { + var otherSegment = nodeA.$outConnections[i]; + if (!otherSegment.$rawSegment) continue; + + var curAngle = rawNode.getAngleToSegment(otherSegment.$rawSegment); + var angle = Math.abs(baseAngle - curAngle); + if (angle > 180) angle = 360 - angle; + + if (30 > angle + && 2 <= angle + && address.isOkFor(120)) { + if (10 > angle) { + segment.report(120); + break; + } + else { + // exclude bow-ties + if (!nodeA.$isPartial + && 3 > nodeA.$otherSegmentsLen + && RR_STREET < typeRank) { + segment.report(120); + break; + } + } + } + } // for all outConnections + } // #120 + + if (nodeB.$outConnectionsLen + // exclude revCons + && (DIR_TWO === direction || DIR_AB === direction) + && isLimitOk(121)) { + var rawNode = nodeB.$rawNode; + var baseAngle = rawNode.getAngleToSegment(rawSegment); + for (var i = 0; i < nodeB.$outConnectionsLen; i++) { + var otherSegment = nodeB.$outConnections[i]; + if (!otherSegment.$rawSegment) continue; + + var curAngle = rawNode.getAngleToSegment(otherSegment.$rawSegment); + var angle = Math.abs(baseAngle - curAngle); + if (angle > 180) angle = 360 - angle; + + if (30 > angle + && 2 <= angle + && address.isOkFor(121)) { + if (10 > angle) { + segment.report(121); + break; + } + else { + // exclude bow-ties + if (!nodeB.$isPartial + && 3 > nodeB.$otherSegmentsLen + && RR_STREET < typeRank) { + segment.report(121); + break; + } + } + } + } // for all outConnections + } // #121 + } // too sharp turns + + // GROUP isDrivable + // Inward Connectivity + if (RT_PRIVATE !== roadType + && isLimitOk(45) + && address.isOkFor(45)) { + if (!nodeA.$isPartial && !nodeA.$inConnectionsLen) { + if (DIR_AB === direction) + segment.report(45); // AB and no in A + else { + if (!nodeB.$isPartial && !nodeB.$inConnectionsLen) + segment.report(45); // no in A and no in B + else { + if (slowChecks + && DIR_TWO === direction + && nodeA.$otherSegmentsLen + && isLimitOk(46)) { + for (var i = 0; i < nodeA.$otherSegmentsLen; i++) { + var otherSegment = nodeA.$otherSegments[i]; + if (!otherSegment.$rawSegment) continue; + + // if(one of other segments at node A is drivable + // AND in connection is possible (two way or dir to node B)) + if (RR_TRAIL < otherSegment.$typeRank + // && RT_PRIVATE !== otherSegment.$type + && (DIR_TWO === otherSegment.$direction + || (DIR_AB === otherSegment.$direction + && nodeAID === otherSegment.$nodeBID) + || (DIR_BA === otherSegment.$direction + && nodeAID === otherSegment.$nodeAID) + )) { + segment.report(46); // no in A, but possible + break; + } + } + } // DIR_TWO + } + } // !DIR_AB + } // no in connections at A + else { + if (!nodeB.$isPartial && !nodeB.$inConnectionsLen) { + if (DIR_BA === direction) + segment.report(45); // BA and no in B + else { + if (slowChecks + && DIR_TWO === direction + && nodeB.$otherSegmentsLen + && isLimitOk(47)) { + for (var i = 0; i < nodeB.$otherSegmentsLen; i++) { + var otherSegment = nodeB.$otherSegments[i]; + if (!otherSegment.$rawSegment) continue; + // if(one of other segments at node B is drivable + // AND in connection is possible (two way or dir to node B)) + if (RR_TRAIL < otherSegment.$typeRank + && (DIR_TWO === otherSegment.$direction + || (DIR_AB === otherSegment.$direction + && nodeBID === otherSegment.$nodeBID) + || (DIR_BA === otherSegment.$direction + && nodeBID === otherSegment.$nodeAID) + )) { + segment.report(47); // no in B, but possible + break; + } + } + } // DIR_TWO + } + } // !nodeB.$inConnectionsLen + } + } // inward connectivity issues + + // GROUP isDrivable + // Outward Connectivity + if (5 < segmentLen + && isLimitOk(44) + && address.isOkFor(44)) { + if (!nodeA.$isPartial && !nodeA.$outConnectionsLen) { + if (DIR_BA === direction) + segment.report(44); // BA and no out A + else { + if (!nodeB.$isPartial && !nodeB.$outConnectionsLen) + segment.report(44); // no out A and no out B + else { + if (slowChecks + && DIR_TWO === direction + && nodeA.$otherSegmentsLen + && isLimitOk(102)) { + for (var i = 0; i < nodeA.$otherSegmentsLen; i++) { + var otherSegment = nodeA.$otherSegments[i]; + if (!otherSegment.$rawSegment) continue; + // if(one of other segments at node A is drivable + // AND no private + // AND out connection is possible (two way or dir to node B)) + if (RR_TRAIL < otherSegment.$typeRank + && RT_PRIVATE !== otherSegment.$type + && (DIR_TWO === otherSegment.$direction + || (DIR_BA === otherSegment.$direction + && nodeAID === otherSegment.$nodeBID) + || (DIR_AB === otherSegment.$direction + && nodeAID === otherSegment.$nodeAID) + )) { + segment.report(102); // no out A, but possible + break; + } + } + } // DIR_TWO + } + } // !DIR_AB + } // no in connections at A + else { + if (!nodeB.$isPartial && !nodeB.$outConnectionsLen) { + if (DIR_AB === direction) + segment.report(44); // AB and no out B + else { + if (slowChecks + && DIR_TWO === direction + && nodeB.$otherSegmentsLen + && isLimitOk(103)) { + for (var i = 0; i < nodeB.$otherSegmentsLen; i++) { + var otherSegment = nodeB.$otherSegments[i]; + if (!otherSegment.$rawSegment) continue; + // if(one of other segments at node B is drivable + // AND ni private + // AND in connection is possible (two way or dir to node B)) + if (RR_TRAIL < otherSegment.$typeRank + && RT_PRIVATE !== otherSegment.$type + && (DIR_TWO === otherSegment.$direction + || (DIR_BA === otherSegment.$direction + && nodeBID === otherSegment.$nodeBID) + || (DIR_AB === otherSegment.$direction + && nodeBID === otherSegment.$nodeAID) + )) { + segment.report(103); // no out B, but possible + break; + } + } + } // DIR_TWO + } + } // !nodeB.$outConnectionsLen + } + } // outward connectivity issues + + // GROUP isDrivable + if (DIR_UNKNOWN === direction + && isLimitOk(25) + && address.isOkFor(25)) + segment.report(25); + + // GROUP isDrivable + if (!(nodeAID && nodeBID) + && isLimitOk(35) + && address.isOkFor(35)) + segment.report(35); + + // GROUP isDrivable + if (RR_PRIMARY > typeRank) { + if (nodeAID && nodeBID + && address.isOkFor(200)) { + if (!segment.$isTurnALocked + && nodeA.$otherSegmentsLen + && isLimitOk(200)) + segment.report(200); + if (!segment.$isTurnBLocked + && nodeB.$otherSegmentsLen + && isLimitOk(300)) + segment.report(300); + } + } + else { + if (nodeAID && nodeBID + && address.isOkFor(201)) { + if (!segment.$isTurnALocked + && nodeA.$otherSegmentsLen + && isLimitOk(201)) + segment.report(201); + if (!segment.$isTurnBLocked + && nodeB.$otherSegmentsLen + && isLimitOk(301)) + segment.report(301); + } + } + + // GROUP isDrivable + if (( + (DIR_AB === direction && nodeA.$outConnectionsLen) + || (DIR_BA === direction && nodeA.$inConnectionsLen) + ) + && isLimitOk(41) + && address.isOkFor(41)) + segment.report(41); + + // GROUP isDrivable + if (( + (DIR_BA === direction && nodeB.$outConnectionsLen) + || (DIR_AB === direction && nodeB.$inConnectionsLen) + ) + && isLimitOk(42) + && address.isOkFor(42)) + segment.report(42); + + // GROUP isDrivable.!nodeApartial + if (!nodeA.$isPartial) { + // GROUP isDrivable.!nodeApartial + if (slowChecks + && 5 < segmentLen + // only for dead-ends + && !nodeA.$otherSegmentsLen + && nodeA.$rawNode.geometry.bounds + && isLimitOk(107) + && address.isOkFor(107)) { + // check if any other segment is close to the node A + var IDs = nodeA.$rawNode.attributes.segIDs; + var pt = new OpenLayers.Geometry.Point( + nodeA.$rawNode.geometry.bounds.left, + nodeA.$rawNode.geometry.bounds.bottom + ); + for (var segKey in WMo.segments.objects) { + var seg = WMo.segments.objects[segKey]; + if (segmentID === seg.getID()) continue; + if (!seg.geometry) continue; + // different elevations + if (elevation !== seg.attributes.level) continue; + // only for non-deleted segments + if ("Delete" === seg.state) continue; + // only for drivable segments + if (RR_TRAIL + >= SimpleSEGMENT.prototype.getTypeRank(seg.attributes.roadType)) + continue; + + // check if node A is not connected to the segment + // only for dead-ends! + if (LIMIT_TOLERANCE > seg.geometry.distanceTo(pt, null)) { + // other segment is not editable + if (!seg.arePropertiesEditable()) + segment.$forceNonEditable = true; + segment.report(107); + break; + } + } + } + + // GROUP isDrivable.!nodeApartial + if (nodeA.$isUturn) { + /* + if(!nodeA.$otherSegmentsLen + && isLimitOk(77) + && address.isOkFor(77)) + segment.report(77); + */ + + if (slowChecks + && 1 === nodeA.$outConnectionsLen + && isLimitOk(99) + && address.isOkFor(99) + && nodeA.$outConnections[0].$isRoundabout) + segment.report(99); + } + + // GROUP isDrivable.!nodeApartial + if (slowChecks + && nodeA.$otherSegmentsLen + && !isRoundabout + && isLimitOk(78) + && address.isOkFor(78)) { + for (var i = 0; i < nodeA.$otherSegmentsLen; i++) { + var otherSegment = nodeA.$otherSegments[i]; + if (!otherSegment.$rawSegment) continue; + // same endpoints + if (RR_TRAIL < otherSegment.$typeRank + && nodeAID && nodeBID + && ( + (otherSegment.$nodeAID === nodeAID + && otherSegment.$nodeBID === nodeBID) + || (otherSegment.$nodeAID === nodeBID + && otherSegment.$nodeBID === nodeAID) + ) + ) { + // report either lower rank or longer segment + if (otherSegment.$typeRank > typeRank + || otherSegment.$length < segmentLen + && otherSegment.$typeRank === typeRank + || otherSegment.$segmentID < segmentID + && otherSegment.$length === segmentLen + && otherSegment.$typeRank === typeRank + ) { + segment.report(78); + break; + } + } + } + } + + // GROUP isDrivable.!nodeApartial.!nodeBpartial + if (!nodeB.$isPartial) { + // GROUP isDrivable.!nodeApartial.!nodeBpartial + options = getCheckOptions(109, countryCode); + if (options[CO_NUMBER] > segmentLen + && nodeA.$otherSegmentsLen + && nodeB.$otherSegmentsLen + && !isRoundabout + && address.isOkFor(109)) + segment.report(109); + + /* + // GROUP isDrivable.!nodeApartial.!nodeBpartial + if(RT_RAMP === roadType + && nodeA.$otherSegmentsLen + && nodeB.$otherSegmentsLen + && RR_RAMP > nodeA.$otherSegments[0].$typeRank + && RR_RAMP > nodeB.$otherSegments[0].$typeRank + && address.isOkFor(CK_RAMPTOSTREET)) + segment.report(CK_RAMPTOSTREET); + */ + + /* + // GROUP isDrivable.!nodeApartial.!nodeBpartial + if(RT_FREEWAY === roadType + && nodeA.$otherSegmentsLen + && nodeB.$otherSegmentsLen + && ( + RR_TRAIL >= nodeA.$otherSegments[0].$typeRank + || RR_TRAIL >= nodeB.$otherSegments[0].$typeRank + || RT_STREET === nodeA.$otherSegments[0].$type + || RT_STREET === nodeB.$otherSegments[0].$type + || RT_PRIMARY === nodeA.$otherSegments[0].$type + || RT_PRIMARY === nodeB.$otherSegments[0].$type + ) + && address.isOkFor(CK_FREEWAYTOSTREET)) + segment.report(CK_FREEWAYTOSTREET); + */ + + // GROUP isDrivable.!nodeApartial.!nodeBpartial + // H - A-B + // A: 2 other, 1 out, 0 or 1 in + // B: 2 other, 1 in, 0 or 1 out + // at least 2 segments with the same street name on A and B + if (slowChecks + && 15 > segmentLen + && !streetLen + // && !isRoundabout + // && RR_STREET < typeRank + // && RT_RAMP !== roadType + && 2 === nodeA.$otherSegmentsLen + && 2 === nodeB.$otherSegmentsLen + && isLimitOk(79) + && address.isOkFor(79) + && nodeA.$otherSegments[0].$rawSegment + && nodeA.$otherSegments[1].$rawSegment + && nodeB.$otherSegments[0].$rawSegment + && nodeB.$otherSegments[1].$rawSegment + && nodeA.$otherSegments[0].$address.$street + + && nodeA.$otherSegments[0].$type + === nodeA.$otherSegments[1].$type + && nodeB.$otherSegments[0].$type + === nodeB.$otherSegments[1].$type + && nodeA.$otherSegments[0].$address.$street + === nodeA.$otherSegments[1].$address.$street + && nodeB.$otherSegments[0].$address.$street + === nodeB.$otherSegments[1].$address.$street + && nodeA.$otherSegments[0].$address.$street + === nodeB.$otherSegments[0].$address.$street + ) { + if ((DIR_TWO === direction || DIR_BA === direction) + && 1 === nodeA.$outConnectionsLen + && 2 > nodeA.$inConnectionsLen + + && 1 === nodeB.$inConnectionsLen + && 2 > nodeB.$outConnectionsLen + ) + segment.report(79); + if ((DIR_TWO === direction || DIR_AB === direction) + && 1 === nodeB.$outConnectionsLen + && 2 > nodeB.$inConnectionsLen + + && 1 === nodeA.$inConnectionsLen + && 2 > nodeA.$outConnectionsLen + ) + segment.report(79); + } // #79 + } // GROUP isDrivable.!nodeApartial.!nodeBpartial + } // GROUP isDrivable.!nodeApartial + + // GROUP isDrivable.!nodeBpartial + if (!nodeB.$isPartial) { + // GROUP isDrivable.!nodeBpartial + if (slowChecks + && 5 < segmentLen + // only for dead-ends + && !nodeB.$otherSegmentsLen + && nodeB.$rawNode.geometry.bounds + && isLimitOk(108) + && address.isOkFor(108)) { + // check if any other segment is close to the node B + var IDs = nodeB.$rawNode.attributes.segIDs; + var pt = new OpenLayers.Geometry.Point( + nodeB.$rawNode.geometry.bounds.left, + nodeB.$rawNode.geometry.bounds.bottom + ); + for (var segKey in WMo.segments.objects) { + var seg = WMo.segments.objects[segKey]; + if (segmentID === seg.getID()) continue; + if (!seg.geometry) continue; + // different elevations + if (elevation !== seg.attributes.level) continue; + // only for non-deleted segments + if ("Delete" === seg.state) continue; + // only for drivable segments + if (RR_TRAIL + >= SimpleSEGMENT.prototype.getTypeRank(seg.attributes.roadType)) + continue; + + if (LIMIT_TOLERANCE > seg.geometry.distanceTo(pt, null)) { + // other segment is not editable + if (!seg.arePropertiesEditable()) + segment.$forceNonEditable = true; + segment.report(108); + break; + } + } + } + + // GROUP isDrivable.!nodeBpartial + if (nodeB.$isUturn) { + if (!nodeB.$otherSegmentsLen + && isLimitOk(77) + && address.isOkFor(77)) + segment.report(77); + + if (slowChecks + && 1 === nodeB.$outConnectionsLen + && isLimitOk(99) + && address.isOkFor(99) + && nodeB.$outConnections[0].$isRoundabout) + segment.report(99); + } + } // GROUP isDrivable.!nodeBpartial + + // GROUP isDrivable && Freeway + if (RT_FREEWAY === roadType) { + // GROUP isDrivable && Freeway + if (0 !== elevation + && isLimitOk(110) + && address.isOkFor(110)) + segment.report(110); + + // GROUP isDrivable && Freeway + options = getCheckOptions(150, countryCode); + if (options[CO_NUMBER] > lock + && isLimitOk(150) + && address.isOkFor(150)) + segment.report(150); + + // GROUP isDrivable && Freeway + if (DIR_TWO === direction + && address.isOkFor(90)) + segment.report(90); + + } // GROUP isDrivable && Freeway + + // GROUP isDrivable && Major + if (RT_MAJOR === roadType) { + // GROUP isDrivable && Major + options = getCheckOptions(151, countryCode); + if (options[CO_NUMBER] > lock + && isLimitOk(151) + && address.isOkFor(151)) + segment.report(151); + } // GROUP isDrivable && Major + + // GROUP isDrivable && Minor + if (RT_MINOR === roadType) { + // GROUP isDrivable && Minor + options = getCheckOptions(152, countryCode); + if (options[CO_NUMBER] > lock + && isLimitOk(152) + && address.isOkFor(152)) + segment.report(152); + } // GROUP isDrivable && Minor + + // GROUP isDrivable && Ramp + if (RT_RAMP === roadType) { + // GROUP isDrivable && Ramp + if (DIR_TWO === direction + && isLimitOk(91) + && address.isOkFor(91)) + segment.report(91); + + // GROUP isDrivable && Ramp + options = getCheckOptions(153, countryCode); + if (options[CO_NUMBER] > lock + && isLimitOk(153) + && address.isOkFor(153)) + segment.report(153); + } // GROUP isDrivable && Ramp + + // GROUP isDrivable && Primary + if (RT_PRIMARY === roadType) { + // GROUP isDrivable && Primary + options = getCheckOptions(154, countryCode); + if (options[CO_NUMBER] > lock + && isLimitOk(154) + && address.isOkFor(154)) + segment.report(154); + } // GROUP isDrivable && Primary + + } // GROUP isDrivable + else { + // GROUP !isDrivable + if (slowChecks + // exclude house numbers + && !hasHNs + // exclude Railroads + && RT_RAILROAD !== roadType + ) { + + // GROUP !isDrivable && slowChecks && !hasHNs && !Railroad + if (nodeA.$otherSegmentsLen + // exclude dead-ends < 300m + // && !nodeB.$isPartial + && (nodeB.$otherSegmentsLen || 300 < segmentLen) + && isLimitOk(114) + && address.isOkFor(114)) { + for (var i = 0; i < nodeA.$otherSegmentsLen; i++) { + var otherSegment = nodeA.$otherSegments[i]; + if (!otherSegment.$rawSegment) continue; + + // if one of other segments at node A is drivable + if (RR_TRAIL < otherSegment.$typeRank) { + segment.report(114); + break; + } + } + } + + // GROUP !isDrivable && slowChecks && !hasHNs && !Railroad + if (nodeB.$otherSegmentsLen + // exclude dead-ends < 300m + // && !nodeA.$isPartial + && (nodeA.$otherSegmentsLen || 300 < segmentLen) + && isLimitOk(115) + && address.isOkFor(115)) { + for (var i = 0; i < nodeB.$otherSegmentsLen; i++) { + var otherSegment = nodeB.$otherSegments[i]; + if (!otherSegment.$rawSegment) continue; + + // if one of other segments at node B is drivable + if (RR_TRAIL < otherSegment.$typeRank) { + segment.report(115); + break; + } + } + } + } // GROUP !isDrivable && slowChecks && !hasHNs && !Railroad + } // GROUP !isDrivable + + /////////////////////////////////////////////////////////////////// + // GROUP streetLen + if (streetLen) { + // GROUP streetLen + // Street type-name checks: {check:type} + /** @const */ + var checkIDType = { + 160: RT_FREEWAY, 161: RT_MAJOR, 162: RT_MINOR, + 163: RT_RAMP, 164: RT_PRIMARY, 165: RT_STREET, 166: RT_PARKING, + 167: RT_RAILROAD, 169: 0 + }; + // mirror checks + /** @const */ + var checkIDID = { 160: 70, 161: 71, 162: 72 }; + for (var i in checkIDType) { + i = +i; + if (!address.isOkFor(i) || !isLimitOk(i)) + continue; + + var rType = checkIDType[i]; + + options = getCheckOptions(i, countryCode); + + if (rType === roadType || !rType) { + if (matchRegExp(i, segmentID, street, options)) + segment.report(i); + } + else { + var mi = checkIDID[i]; + if (mi + && address.isOkFor(mi) + && !matchRegExp(i, segmentID, street, options)) + segment.report(mi); + } + } // for typeName + + // GROUP streetLen + // RegExp street name checks + for (var i = CK_STREETNAMEFIRST; i <= CK_STREETNAMELAST; i++) { + if (!address.isOkFor(i) || !isLimitOk(i)) + continue; + + if (matchRegExp(i, segmentID, street, + getCheckOptions(i, countryCode))) + segment.report(i); + } // for street name checks + + // GROUP streetLen + if (cityLen + && RT_RAMP === roadType + && isLimitOk(57) + && address.isOkFor(57)) + segment.report(57); + + // GROUP streetLen + if (-1 !== street.indexOf("CONST ZN") + && isLimitOk(117) + && address.isOkFor(117)) + segment.report(117); + + // GROUP streetLen + if (RT_RAMP !== roadType + && -1 !== street.indexOf('.') + && isLimitOk(95) + && address.isOkFor(95)) + segment.report(95); + + // GROUP streetLen && Ramp + if (RT_RAMP === roadType) { + // GROUP streetLen && Ramp + if (DIR_TWO === direction) { + if (isLimitOk(28) + && address.isOkFor(28)) + segment.report(28); + } + } // GROUP streetLen && Ramp + + // GROUP streetLen + if (RR_RAMP > typeRank) { + options = getCheckOptions(73, countryCode); + if (options[CO_NUMBER] > streetLen + && isLimitOk(73) + && address.isOkFor(73)) + segment.report(73); + } + + // GROUP streetLen + if (isDrivable) { + if (RT_RAMP === roadType) { + options = getCheckOptions(112, countryCode); + if (options[CO_NUMBER] < streetLen + && isLimitOk(112) + && address.isOkFor(112)) + segment.report(112); + } + else { + options = getCheckOptions(52, countryCode); + if (options[CO_NUMBER] < streetLen + && isLimitOk(52) + && address.isOkFor(52)) + segment.report(52); + } + } + } // GROUP streetLen + + /////////////////////////////////////////////////////////////////// + // GROUP isRoundabout && isDrivable + if (isRoundabout && isDrivable) { + // GROUP isRoundabout + if (streetLen + && isLimitOk(29) + && address.isOkFor(29)) + segment.report(29); + + // GROUP isRoundabout + if (DIR_TWO === direction + && address.isOkFor(48)) + segment.report(48); + + // GROUP isRoundabout + if (!nodeA.$isPartial && 2 < nodeA.$otherSegmentsLen) { + if (2 < nodeA.$outConnectionsLen) { + if (address.isOkFor(87)) + segment.report(87); + } + else { + if (address.isOkFor(74)) + segment.report(74); + } + } + + // GROUP isRoundabout + if (slowChecks + && !isPartial + && (DIR_AB === direction || DIR_BA === direction) + ) { + // GROUP isRoundabout.loops + // check if roundabout connected to another roundabout + var okA = false; + var okB = false; + var anode, bnode; + if (DIR_AB === direction) + anode = nodeA, bnode = nodeB; + else + anode = nodeB, bnode = nodeA; + for (var i = 0; i < bnode.$outConnectionsLen; i++) { + var otherSegment = bnode.$outConnections[i]; + if (otherSegment.$isRoundabout) { + okB = true; + break; + } + } + if (okB) + for (var i = 0; i < anode.$inConnectionsLen; i++) { + var otherSegment = anode.$inConnections[i]; + if (otherSegment.$isRoundabout) { + okA = true; + break; + } + } + + // GROUP isRoundabout.loops + if ((!okB || !okA) + && address.isOkFor(50)) + segment.report(50); + } // GROUP isRoundabout.loops + + } // GROUP isRoundabout + + + // highlight reported segments + HLSegment(rawSegment); + } // for all segments + + // update severity if needed + if (bUpdateMaxSeverity + && (RTStateIs(ST_STOP) || RTStateIs(ST_PAUSE))) + async(F_SHOWREPORT, RF_UPDATEMAXSEVERITY); + + // update segment properties + updateSegmentProperties(selectedSegments, false); + + // add HLed segments to the layer + addHLedSegments(); +}